파일이 사용 중인지 확인하는 방법이 있습니까? 사용중인 파일” 오류가

1 이미지 파일에 반복적으로 액세스 해야하는 프로그램을 C #으로 작성 중입니다. 대부분의 경우 작동하지만 내 컴퓨터가 빠르게 실행되는 경우 파일 시스템에 다시 저장하기 전에 파일에 액세스하려고 시도하고 “다른 프로세스에서 사용중인 파일” 오류가 발생 합니다.

이 문제를 해결할 방법을 찾고 싶지만 내 모든 인터넷 검색은 예외 처리를 사용하여 검사를 만들었습니다. 이것은 내 종교에 위배되므로 누군가 더 좋은 방법이 있는지 궁금합니다.



답변

이 솔루션에 대한 업데이트 된 참고 : FileAccess.ReadWrite파일을 검사 하면 읽기 전용 파일이 검사 되지 않으므로 솔루션을 검사하도록 수정되었습니다 FileAccess.Read. FileAccess.Read파일에 쓰기 또는 읽기 잠금이있는 경우 확인 시도 가 실패 하기 때문에이 솔루션이 작동하지만 파일에 쓰기 또는 읽기 잠금이없는 경우 (예 : 파일이 열려있는 경우)이 솔루션이 작동하지 않습니다. FileShare.Read 또는 FileShare.Write 액세스 권한으로 (읽기 또는 쓰기 용)

ORIGINAL :
지난 몇 년 동안이 코드를 사용해 왔으며 아무런 문제가 없었습니다.

예외 사용에 대한 망설임을 이해하지만 항상 예외를 피할 수는 없습니다.

protected virtual bool IsFileLocked(FileInfo file)
{
    try
    {
        using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
        {
            stream.Close();
        }
    }
    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;
    }

    //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");


답변

의도 한대로 예외를 사용하십시오. 파일이 사용 중임을 승인하고 조치가 완료 될 때까지 반복하여 다시 시도하십시오. 또한 작동하기 전에 상태를 확인하는주기를 낭비하지 않기 때문에 가장 효율적입니다.

예를 들어 아래 기능을 사용하십시오.

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

2 초 후에 시간 초과되는 재사용 가능한 방법

private T TimeoutFileAction<T>(Func<T> 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);
}


답변

아마도 FileSystemWatcher를 사용 하여 Changed 이벤트를 감시 할 수 있습니다 .

나는 이것을 직접 사용하지는 않았지만 기회가 될 수도 있습니다. 이 경우 파일 시스템 감시자가 약간 무겁다면 try / catch / sleep 루프를 사용합니다.


답변

스트림이 제공되는 즉시 스트림을 제공하는 작업을 반환 할 수 있습니다. 간단한 솔루션이지만 좋은 출발점입니다. 스레드 안전합니다.

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

평소처럼이 스트림을 사용할 수 있습니다.

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


답변

내가 아는 유일한 방법은 너무 빠르지 않지만 예제가있는 Win32 독점 잠금 API를 사용하는 것입니다.

대부분의 사람들은 이것에 대한 간단한 해결책을 위해 단순히 루프를 시도 / 잡기 / 수면합니다.