Есть ли способ проверить, используется ли файл?

Я пишу программу на C #, которая требует многократного доступа к 1 файлу изображения. В большинстве случаев он работает, но если мой компьютер работает быстро, он попытается получить доступ к файлу, прежде чем он будет сохранен обратно в файловую систему, и выкинет ошибку: «Файл используется другим процессом» .

Я хотел бы найти способ обойти это, но весь мой Googling только дал возможность создавать проверки с помощью обработки исключений. Это противоречит моей религии, поэтому мне было интересно, есть ли у кого лучший способ сделать это?

15 Solutions collect form web for “Есть ли способ проверить, используется ли файл?”

Обновлено ПРИМЕЧАНИЕ к этому решению : проверка с помощью FileAccess.ReadWrite завершится с ошибкой для файлов только для чтения, поэтому решение было изменено для проверки с помощью FileAccess.Read . Хотя это решение работает, потому что попытка проверить с помощью FileAccess.Read завершится с ошибкой, если в файле есть блокировка записи или чтения, однако это решение не будет работать, если в файле нет блокировки записи или чтения, т. Е. (для чтения или записи) с доступом FileShare.Read или FileShare.Write.

ОРИГИНАЛ: Я использовал этот код в течение последних нескольких лет, и у меня не было никаких проблем с ним.

Понимайте свое колебание в использовании исключений, но вы не можете избежать их все время:

 protected virtual bool IsFileLocked(FileInfo file) { FileStream stream = null; try { stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None); } catch (IOException) { //the file is unavailable because it is: //still being written to //or being processed by another thread //or does not exist (has already been processed) return true; } finally { if (stream != null) stream.Close(); } //file is not locked return false; } 

Вы можете пострадать от состояния гонки нитей, на котором есть документированные примеры того, как это используется как уязвимость безопасности. Если вы проверите, что файл доступен, но затем попробуйте его использовать, вы можете бросить в тот момент, который злоумышленник может использовать для принудительного использования и использования в вашем коде.

Лучше всего попробовать try catch / finally, который пытается получить дескриптор файла.

 try { using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open)) { // File/Stream manipulating code here } } catch { //check here why it failed and ask user to retry if the file is in use. } 

Используйте это, чтобы проверить, заблокирован ли файл:

 using System.IO; using System.Runtime.InteropServices; internal static class Helper { const int ERROR_SHARING_VIOLATION = 32; const int ERROR_LOCK_VIOLATION = 33; private static bool IsFileLocked(Exception exception) { int errorCode = Marshal.GetHRForException(exception) & ((1 < < 16) - 1); return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION; } internal static bool CanReadFile(string filePath) { //Try-Catch so we dont crash the program and can check the exception try { //The "using" is important because FileStream implements IDisposable and //"using" will avoid a heap exhaustion situation when too many handles //are left undisposed. using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { if (fileStream != null) fileStream.Close(); //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway! } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file return false; } } finally { } return true; } } 

По соображениям производительности я рекомендую вам прочитать содержимое файла в той же операции. Вот некоторые примеры:

 public static byte[] ReadFileBytes(string filePath) { byte[] buffer = null; try { using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { int length = (int)fileStream.Length; // get file length buffer = new byte[length]; // create buffer int count; // actual number of bytes read int sum = 0; // total number of bytes read // read until Read method returns 0 (end of the stream has been reached) while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) sum += count; // sum is a buffer offset for next reading fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something? } } catch (Exception ex) { } finally { } return buffer; } public static string ReadFileTextWithEncoding(string filePath) { string fileContents = string.Empty; byte[] buffer; try { using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { int length = (int)fileStream.Length; // get file length buffer = new byte[length]; // create buffer int count; // actual number of bytes read int sum = 0; // total number of bytes read // read until Read method returns 0 (end of the stream has been reached) while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) { sum += count; // sum is a buffer offset for next reading } fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP //Depending on the encoding you wish to use - I'll leave that up to you fileContents = System.Text.Encoding.Default.GetString(buffer); } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something? } } catch (Exception ex) { } finally { } return fileContents; } public static string ReadFileTextNoEncoding(string filePath) { string fileContents = string.Empty; byte[] buffer; try { using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { int length = (int)fileStream.Length; // get file length buffer = new byte[length]; // create buffer int count; // actual number of bytes read int sum = 0; // total number of bytes read // read until Read method returns 0 (end of the stream has been reached) while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) { sum += count; // sum is a buffer offset for next reading } fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP char[] chars = new char[buffer.Length / sizeof(char) + 1]; System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length); fileContents = new string(chars); } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something? } } catch (Exception ex) { } finally { } return fileContents; } 

Попробуйте сами:

 byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt"); string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt"); string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt"); 

Возможно, вы можете использовать FileSystemWatcher и следить за событием Changed.

Я не использовал это сам, но это может стоить того. Если для этого случая файловая система будет немного тяжелой, я бы пошел на цикл try / catch / sleep.

единственный способ, которым я знаю, – использовать API исключительной блокировки Win32, который не слишком быстр, но существуют примеры.

Большинство людей, для простого решения этого, просто пытаются / уловить / спать.

 static bool FileInUse(string path) { try { using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate)) { fs.CanWrite } return false; } catch (IOException ex) { return true; } } string filePath = "C:\\Documents And Settings\\yourfilename"; bool isFileInUse; isFileInUse = FileInUse(filePath); // Then you can do some checking if (isFileInUse) Console.WriteLine("File is in use"); else Console.WriteLine("File is not in use"); 

Надеюсь это поможет!

Просто используйте исключение, как предполагалось. Примите, что файл используется, и повторите попытку, пока ваше действие не будет завершено. Это также наиболее эффективно, потому что вы не тратите никаких циклов на проверку состояния перед тем, как действовать.

Используйте функцию ниже, например

 TimeoutFileAction(() => { System.IO.File.etc...; return null; } ); 

Многоразовый метод, который истекает через 2 секунды

 private T TimeoutFileAction(Func func) { var started = DateTime.UtcNow; while ((DateTime.UtcNow - started).TotalMilliseconds < 2000) { try { return func(); } catch (System.IO.IOException exception) { //ignore, or log somewhere if you want to } } return default(T); } 

Вы можете вернуть задачу, которая дает вам stream, как только он станет доступным. Это упрощенное решение, но это хорошая отправная точка. Это streamобезопасный.

 private async Task GetStreamAsync() { try { return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write); } catch (IOException) { await Task.Delay(TimeSpan.FromSeconds(1)); return await GetStreamAsync(); } } 

Вы можете использовать этот stream, как обычно:

 using (var stream = await FileStreamGetter.GetStreamAsync()) { Console.WriteLine(stream.Length); } 

Принятые ответы выше имеют проблему, если файл был открыт для записи в режиме FileShare.Read или если у файла есть атрибут «Только для чтения», код не будет работать. Это модифицированное решение работает наиболее надежно, с учетом двух вещей (как и для принятого решения):

  1. Он не будет работать для файлов, которые были открыты в режиме совместного доступа к записи
  2. Это не учитывает проблемы с streamами, поэтому вам придется блокировать их или обрабатывать проблемы с streamами отдельно.

Помня об этом, это проверяет, заблокирован ли файл для записи или заблокирован для предотвращения чтения :

 public static bool FileLocked(string FileName) { FileStream fs = null; try { // NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it's locked by another process for writing } catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx { // This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode try { fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None); } catch (Exception) { return true; // This file has been locked, we can't even open it to read } } catch (Exception) { return true; // This file has been locked } finally { if (fs != null) fs.Close(); } return false; } 

Вот какой-то код, который, насколько я могу лучше всего сказать, делает то же самое, что и принятый ответ, но с меньшим количеством кода:

  public static bool IsFileLocked(string file) { try { using (var stream = File.OpenRead(file)) return false; } catch (IOException) { return true; } } 

Однако я считаю, что это более важно сделать следующим образом:

  public static void TryToDoWithFileStream(string file, Action action, int count, int msecTimeOut) { FileStream stream = null; for (var i = 0; i < count; ++i) { try { stream = File.OpenRead(file); break; } catch (IOException) { Thread.Sleep(msecTimeOut); } } action(stream); } 

Вы можете использовать мою библиотеку для доступа к файлам из нескольких приложений.

Вы можете установить его из nuget: Install-Package Xabe.FileLock

Если вы хотите получить дополнительную информацию об этом, проверьте https://github.com/tomaszzmuda/Xabe.FileLock

 ILock fileLock = new FileLock(file); if(fileLock.Acquire(TimeSpan.FromSeconds(15), true)) { using(fileLock) { // file operations here } } 

Метод fileLock.Acquire вернет true только в том случае, если блокировка файла исключена для этого объекта. Но приложение, загружающее файл, должно делать это и в блокировке файлов. Если объект недоступен, метод возвращает false.

По моему опыту, вы обычно это делаете, затем «защищаете» свои файлы, чтобы что-то притворяться, а затем используйте «защищенные» файлы. Если у вас есть только один файл, который вы хотите использовать таким образом, вы можете использовать трюк, который объясняется в ответ Джереми Томпсоном. Однако, если вы попытаетесь сделать это на большом количестве файлов (скажем, например, когда вы пишете установщик), вам будет очень больно.

Очень элегантный способ, который можно решить, заключается в том, что ваша файловая система не позволит вам изменять имя папки, если один из файлов там используется. Храните папку в той же файловой системе, и она будет работать как очарование.

Обратите внимание, что вы должны знать об очевидных способах использования этой возможности. В конце концов, файлы не будут заблокированы. Кроме того, имейте в виду, что есть другие причины, которые могут привести к сбою операции Move . Очевидно, что правильная обработка ошибок (MSDN) может помочь здесь.

 var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N")); try { Directory.Move(originalFolder, someFolder); // Use files } catch // TODO: proper exception handling { // Inform user, take action } finally { Directory.Move(someFolder, originalFolder); } 

Для отдельных файлов я бы придерживался предложения о блокировке, опубликованного Джереми Томпсоном.

Мне интересно узнать, вызывает ли это любые рефлексы WTF. У меня есть процесс, который создает и впоследствии запускает PDF-документ из консольного приложения. Тем не менее, я имел дело с хрупкостью, когда пользователь должен был запускать процесс несколько раз, генерируя тот же файл без первого закрытия ранее сгенерированного файла, приложение будет генерировать исключение и умереть. Это было довольно частое явление, поскольку имена файлов основаны на номерах продаж.

Вместо того, чтобы терпеть неудачу в такой непристойной манере, я решил полагаться на автоматическое инкрементное управление версиями файлов:

 private static string WriteFileToDisk(byte[] data, string fileName, int version = 0) { try { var versionExtension = version > 0 ? $"_{version:000}" : string.Empty; var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf"); using (var writer = new FileStream(filePath, FileMode.Create)) { writer.Write(data, 0, data.Length); } return filePath; } catch (IOException) { return WriteFileToDisk(data, fileName, ++version); } } 

Вероятно, некоторые дополнительные меры предосторожности могут быть catch блоку catch для обеспечения того, что я catch правильные исключения IOException. Я, вероятно, также очищу хранилище приложений при запуске, так как эти файлы должны быть временными в любом случае.

Я понимаю, что это выходит за frameworks вопроса OP о простом проверке использования файла, но это была действительно проблема, которую я искал, когда я приехал сюда, поэтому, возможно, это будет полезно кому-то другому.

Попробуйте переместить / скопировать файл в папку temp. Если вы можете, у него нет блокировки, и вы можете безопасно работать во временном каталоге без получения блокировок. Else просто попробуйте переместить его за x секунд.

Я использую это обходное решение, но у меня есть промежуток времени между тем, когда я проверяю блокировку файла с помощью функции IsFileLocked и когда я открываю файл. В этот промежуток времени какой-то другой stream может открыть файл, поэтому я получу IOException.

Итак, я добавил для этого дополнительный код. В моем случае я хочу загрузить XDocument:

  XDocument xDoc = null; while (xDoc == null) { while (IsFileBeingUsed(_interactionXMLPath)) { Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait..."); Thread.Sleep(100); } try { xDoc = XDocument.Load(_interactionXMLPath); } catch { Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!"); } } 

Как вы думаете? Могу ли я что-то изменить? Может быть, мне не нужно было использовать функцию IsFileBeingUsed?

благодаря

Interesting Posts

Предоставление разрешения на запись нескольким пользователям в папке в Ubuntu

Не удается получить доступ к определенным веб-сайтам – сбросить маршрутизатор, какие-либо идеи?

Загрузите текст / plain вместо открытия в браузере

Как я могу совместно использовать принтер в Windows 7 x64 с Windows XP x86?

Автоматически менять громкость системы при подключении аудиоустройства?

Почему gmail считает, что почта, которую отправляет мой сервер, является спамом?

Одна пара ключей GnuPG / PGP, два письма?

В Excel 2010, как я могу создать шаблон с функцией, автоматически заканчивается в последней ячейке с данными?

Outlook начинает получать и отправлять письма в двух экземплярах и в трех экземплярах

Какова цель портов?

Отключить предупреждение безопасности для сайтов Intranet

«Открыть» / «Сохранить как», что приведет к сбою программ в Windows 7

Можно ли запускать несколько виртуальных машин с независимой клавиатурой мыши + на одном ПК?

Удалить все строки в Notepad ++, кроме строк, содержащих нужное мне слово?

Как удалить дубликаты записей в Excel 2007

Давайте будем гением компьютера.