Estou escrevendo um programa em c # que precisa acessar repetidamente 1 arquivo de imagem. Na maioria das vezes, ele funciona, mas se meu computador estiver executando rapidamente, ele tentará acessar o arquivo antes de ser salvo no sistema de arquivos e lançará um erro: "Arquivo em uso por outro processo" .
Gostaria de encontrar uma maneira de contornar isso, mas todo o meu Google apenas produziu verificações usando o tratamento de exceções. Isso é contra a minha religião, então eu queria saber se alguém tem uma maneira melhor de fazê-lo.
Respostas:
NOTA atualizada sobre esta solução : A verificação com
FileAccess.ReadWrite
falha de arquivos somente leitura, portanto a solução foi modificada para verificaçãoFileAccess.Read
. Embora esta solução funcione porque tentar verificar comFileAccess.Read
ele falhará se o arquivo tiver um bloqueio de gravação ou leitura, no entanto, esta solução não funcionará se o arquivo não tiver um bloqueio de gravação ou leitura, ou seja, foi aberto (para leitura ou gravação) com o acesso FileShare.Read ou FileShare.Write.ORIGINAL: usei esse código nos últimos anos e não tive problemas com ele.
Entenda sua hesitação em usar exceções, mas você não pode evitá-las o tempo todo:
fonte
public static bool IsLocked(this FileInfo file) {/*...*/}
.Você pode sofrer com uma condição de corrida de encadeamento, onde existem exemplos documentados disso sendo usado como uma vulnerabilidade de segurança. Se você verificar se o arquivo está disponível, mas tentar usá-lo, poderá lançar nesse ponto, que um usuário mal-intencionado pode usar para forçar e explorar seu código.
Sua melhor aposta é uma tentativa de captura / finalmente, que tenta entender o arquivo.
fonte
Use isto para verificar se um arquivo está bloqueado:
Por motivos de desempenho, recomendo que você leia o conteúdo do arquivo na mesma operação. aqui estão alguns exemplos:
Experimente você mesmo:
fonte
IOException
, em vez de geral,Exception
e depois um teste no tipo.IOException
após o geral. O geral vai pegar tudo que passa e o específicoIOException
sempre será solitário. Apenas troque os dois.Basta usar a exceção como pretendido. Aceite que o arquivo esteja em uso e tente novamente várias vezes até que sua ação seja concluída. Isso também é o mais eficiente, porque você não perde nenhum ciclo verificando o estado antes de agir.
Use a função abaixo, por exemplo
Método reutilizável que atinge o tempo limite após 2 segundos
fonte
Talvez você possa usar um FileSystemWatcher e assistir ao evento Changed.
Eu não usei isso sozinho, mas pode valer a pena tentar. Se o file systemwatcher for um pouco pesado para este caso, eu usaria o loop try / catch / sleep.
fonte
Você pode retornar uma tarefa que fornece um fluxo assim que estiver disponível. É uma solução simplificada, mas é um bom ponto de partida. É thread-safe.
Você pode usar esse fluxo como de costume:
fonte
GetStreamAsync()
?a única maneira que conheço é usar a API de bloqueio exclusivo do Win32, que não é muito rápida, mas existem exemplos.
A maioria das pessoas, para uma solução simples para isso, simplesmente tenta / pega / dorme os loops.
fonte
Espero que isto ajude!
fonte
As respostas aceitas acima sofrem um problema em que, se o arquivo tiver sido aberto para gravação no modo FileShare.Read ou se o arquivo tiver um atributo Somente Leitura, o código não funcionará. Essa solução modificada funciona de maneira mais confiável, com duas coisas a serem lembradas (como também é verdade para a solução aceita):
Tendo em mente o acima exposto, isso verifica se o arquivo está bloqueado para gravação ou bloqueado para impedir a leitura :
fonte
Além de trabalhar com três linhas e apenas para referência: se você quiser informações completas - há um pequeno projeto no Microsoft Dev Center:
https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4
Da introdução:
Funciona conectando-se à "Reiniciar sessão do gerenciador".
Pode ser um pouco complicado para suas necessidades específicas ... Mas se é isso que você deseja, vá em frente e pegue o vs-project.
fonte
Na minha experiência, você geralmente quer fazer isso, depois 'protege' seus arquivos para fazer algo sofisticado e depois usa os arquivos 'protegidos'. Se você tiver apenas um arquivo que queira usar assim, poderá usar o truque explicado na resposta de Jeremy Thompson. No entanto, se você tentar fazer isso em vários arquivos (por exemplo, quando estiver escrevendo um instalador), sofrerá bastante.
Uma maneira muito elegante de resolver isso é usar o fato de que seu sistema de arquivos não permitirá que você altere o nome de uma pasta se um dos arquivos lá estiver sendo usado. Mantenha a pasta no mesmo sistema de arquivos e funcionará como um encanto.
Observe que você deve estar ciente das maneiras óbvias de que isso possa ser explorado. Afinal, os arquivos não serão bloqueados. Além disso, esteja ciente de que existem outros motivos que podem resultar na
Move
falha da sua operação. Obviamente, o tratamento adequado de erros (MSDN) pode ajudar aqui.Para arquivos individuais, eu continuaria com a sugestão de bloqueio postada por Jeremy Thompson.
fonte
FileShare
e da verificação de um bloqueio.Aqui está um código que, tanto quanto eu posso dizer, faz a mesma coisa que a resposta aceita, mas com menos código:
No entanto, acho que é mais robusto fazê-lo da seguinte maneira:
fonte
Você pode usar minha biblioteca para acessar arquivos de vários aplicativos.
Você pode instalá-lo a partir do nuget: Install-Package Xabe.FileLock
Se você quiser obter mais informações, consulte https://github.com/tomaszzmuda/Xabe.FileLock
O método fileLock.Acquire retornará true somente se puder bloquear o arquivo exclusivo para esse objeto. Mas o aplicativo que carrega o arquivo também deve fazê-lo no bloqueio de arquivo. Se o objeto estiver inacessível, o método retornará false.
fonte
Uma vez eu precisei enviar PDFs para um arquivo de backup online. Mas o backup falharia se o usuário tivesse o arquivo aberto em outro programa (como o leitor de PDF). Na pressa, tentei algumas das principais respostas deste tópico, mas não consegui fazê-las funcionar. O que funcionou para mim foi tentar mover o arquivo PDF para seu próprio diretório . Descobri que isso falharia se o arquivo fosse aberto em outro programa e se a movimentação fosse bem-sucedida, não haveria operação de restauração necessária, como aconteceria se fosse movido para um diretório separado. Quero postar minha solução básica, caso seja útil para casos de uso específicos de outras pessoas.
fonte
Estou interessado em ver se isso desencadeia algum reflexo WTF. Eu tenho um processo que cria e, posteriormente, inicia um documento PDF a partir de um aplicativo de console. No entanto, eu estava lidando com uma fragilidade em que, se o usuário executasse o processo várias vezes, gerando o mesmo arquivo sem primeiro fechar o arquivo gerado anteriormente, o aplicativo lançaria uma exceção e morreria. Essa foi uma ocorrência bastante frequente, porque os nomes dos arquivos são baseados nos números das cotações de vendas.
Em vez de falhar de uma maneira tão desagradável, decidi confiar no versionamento de arquivo incrementado automaticamente:
Provavelmente, um pouco mais de cuidado pode ser dado ao
catch
bloco para garantir que eu esteja capturando as IOException (s) corretas. Provavelmente também limparei o armazenamento do aplicativo na inicialização, pois esses arquivos devem ser temporários de qualquer maneira.Sei que isso vai além do escopo da questão do OP de simplesmente verificar se o arquivo está em uso, mas esse era realmente o problema que eu estava procurando resolver quando cheguei aqui; talvez seja útil para outra pessoa.
fonte
Algo assim ajudaria?
fonte
Tente mover / copiar o arquivo para um diretório temporário. Se você puder, ele não tem bloqueio e você pode trabalhar com segurança no diretório temp sem obter bloqueios. Caso contrário, tente movê-lo novamente em x segundos.
fonte
Eu uso essa solução alternativa, mas tenho um intervalo de tempo entre quando eu verifico o bloqueio do arquivo com a função IsFileLocked e quando abro o arquivo. Nesse período, algum outro thread pode abrir o arquivo, então eu receberei IOException.
Então, eu adicionei um código extra para isso. No meu caso, quero carregar o XDocument:
O que você acha? Posso mudar alguma coisa? Talvez eu não precise usar a função IsFileBeingUsed?
obrigado
fonte