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


846

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

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


30
시스템의 모든 열린 핸들을 검사하여 테스트 할 수 있습니다. 그러나 Windows는 멀티 태스킹 운영 체제이므로 코드를 실행 한 후 파일이 열려 있고 열려 있지 않은 것으로 판단되면 프로세스 코드가 해당 파일을 사용하기 시작한 다음 시도 할 때까지 가능성이 있습니다. 사용하면 오류가 발생합니다. 그러나 먼저 확인하는 데 아무런 문제가 없습니다. 실제로 필요할 때 사용하지 않는다고 가정하지 마십시오.
BobbyShaftoe

3
그러나이 특정 문제에 대해서만; 파일 핸들을 검사하지 말고 미리 설정 한 횟수만큼 시도하십시오 (예 : 실패하기 전에 3-5).
BobbyShaftoe

이 이미지 파일은 어떻게 생성됩니까? 생성이 완료 될 때까지 프로그램을 중지 / 잠자기 / 일시 정지 할 수 있습니까? 그것은 상황을 다루는 훨씬 더 좋은 방법입니다. 그렇지 않다면 예외 처리를 피할 수 있다고 생각하지 않습니다.
Catchwa

의도적으로 실패 가능성을 배제하지 않으면 서 잠재적으로 위험한 것을 수행하여 예외를 모두 사용하는 것이 가정에 대한 점검이 아닙니까?
jwg

26
당신의 철학은 예외에 대한 나쁜 이해입니다. 대부분의 사람들은 예외가 엉망으로 된 무언가를 의미한다고 생각합니다. 예외가 .... 예외를 의미하는 경우. 이는 "처리"해야하는 예외적 인 상황을 의미합니다. 데이터 액세스를 위해 계속 재 시도하고 싶을 수도 있으며, 사용자는 연결을 할 수 없다는 것을 알아야합니다. 너 뭐하니? ConnectionFailedException을 처리하고 사용자에게 알리므로 한 시간 후에 시도가 중단되고 케이블이 분리 된 것을 알 수 있습니다.
Lee Louviere

답변:


542

이 솔루션에 대한 업데이트 된 참고 : 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;
}

60
이것은 훌륭한 해결책이지만 한 가지 의견이 있습니다. 파일이 읽기 전용 인 경우 ReadWrite는 항상 실패하기 때문에 액세스 모드 FileAccess.Read로 파일을 열지 않을 수 있습니다.
adeel825

220
-1. IsFileLocked에서 파일을 닫은 후 스레드가 파일을 열기 전에 다른 스레드 / 프로세스에 의해 파일이 잠길 수 있기 때문에 잘못된 답변입니다.
Polyfun

16
나는 이것이 큰 대답이라고 생각합니다. int로서 나는이 사용하고 확장 방법은public static bool IsLocked(this FileInfo file) {/*...*/}.
Manuzor

54
@ChrisW : 무슨 일인지 궁금 할 것입니다. 놀라지 마십시오. 당신은 단지 Daily WTF 공동체의 분노에 노출되고 있습니다 : thedailywtf.com/Comments/…
Pierre Lebeaupin

16
@ChrisW 왜 그렇게 나쁜가. 이 커뮤니티는 좋은 대답과 나쁜 대답을 지적하기 위해 여기에 있습니다. 많은 전문가가 이것이 나쁜 일임을 인식하고 downvote에 가입하면 사이트는 WAI입니다. 그리고 당신이 부정적인 말을하기 전에, 당신이 그 기사를 읽으면, 그들은 "올바른 대답을 찬성"한다고 잘못 답하지 않습니다. 그들의 의견을 의견으로 설명하길 원하십니까? 다른 좋은 사이트를 소개해 주셔서 감사합니다!
Lee Louviere 2016 년

569

보안 취약점으로 사용되는 문서화 된 예제가있는 스레드 경쟁 조건이 발생할 수 있습니다. 파일이 사용 가능한지 확인한 다음 파일을 사용하여 사용하려고하면 악의적 인 사용자가 코드를 강제로 이용하고 악용 할 수있는 시점에이를 던질 수 있습니다.

가장 좋은 방법은 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.
}

124
+1. 확인 후 밀리 초 후에 파일을 더 이상 사용하지 않거나 그 반대의 경우도 있으므로 "파일을 사용중인 경우 학습"하는 100 % 안전한 방법은 없습니다. 대신 예외가없는 경우 파일을 열고 사용하면됩니다.
Sedat Kapanoglu

8
너무 나쁜 .NET은 CAS를 지원하지 않습니다. 성공 / 실패를 반환하는 TryOpenFile (Ref FileHandle)과 같은 것. 예외 처리에만 의존하지 않는 해결 방법이 항상 있어야합니다. Microsoft Office가 어떻게하는지 궁금합니다.
TamusJRoyce

2
여기서 이해해야 할 핵심은이 API는 Windows API를 사용하여 파일 핸들을 얻는 것입니다. 따라서 C API에서 수신 한 오류 코드를 변환하고 예외로 랩핑하여 처리해야합니다. .Net에는 예외 처리가 있으므로 사용하지 않는 것이 좋습니다. 그렇게하면 코드에 깔끔한 전달 경로를 작성하고 오류 처리를 별도의 코드 경로에 남겨 둘 수 있습니다.
Spence

36
using 문은 완료된 후에 스트림이 닫히도록하는 것입니다. using () {}이 {} try {} finally {obj.Dispose ()}보다 문자 수가 적다는 것을 알 수 있습니다. 또한 using 문 외부에서 객체 참조를 선언해야한다는 것을 알 수 있습니다. 명시 적 인터페이스가 있다면 캐스팅해야합니다. 마지막으로 최대한 빨리 폐기하고 마지막으로 논리에는 UI 또는 IDispose 호출과 관련이없는 다른 장기 실행 작업이있을 수 있습니다. </ rant>
Spence

2
시도 외부에서 객체를 선언해야하고 dispose를 명시 적으로 호출해야한다는 사실을 부정하지 않습니다.
Spence

92

이를 사용하여 파일이 잠겨 있는지 확인하십시오.

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

8
거기에 너무 많은 "매직 넘버"가 아니었다면 나는 찬성 투표 것 ) en.wikipedia.org/wiki/Magic_number_(programming
크리스가

3
비트 시프트가 아니라 errorCode 비교를 언급했습니다. 하지만 지금 당신은 그것을 언급 ...
크리스

1
Catch는 IOException일반 대신에 켜져 있어야하며 Exception유형에 대한 테스트 여야합니다 .
Askolein

3
@JeremyThompson 슬프게도 당신은 IOException일반적인 것을 따랐습니다 . 일반적인 것은 지나가는 모든 것을 잡을 것이고 특정 IOException은 항상 외로울 것입니다. 그냥 둘을 바꾸십시오.
Askolein

나는이 해결책을 좋아한다. 또 다른 제안 : if (IsFileLocked (ex))에 대한 캐치 내부에서 ex를 던질 것입니다. 그러면 예외가 발생하여 파일이 존재하지 않는 (또는 다른 IOException) 경우를 처리합니다.
shindigo

7

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

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

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);
}

6

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

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


1
파일 작성 / 변경 시작시 작성 및 변경 이벤트가 발생하므로 FileSystemWatcher를 사용하면 도움이되지 않습니다. 작은 파일이라도 .NET 응용 프로그램이 FileSystemEventHandler 콜백을 통해 실행하는 것보다 운영 체제에서 작성하고 닫는 데 더 많은 시간이 필요합니다. 이것은 매우 슬프지만 파일에 액세스하거나 예외 루프를 실행하기 전에 대기 시간을 추정하는 것 외에 다른 옵션은 없습니다.

FileSystemWatcher는 많은 변경 사항을 동시에 잘 처리하지 못하므로 조심하십시오.
Ben F

1
BTW, MS가 자신의 FSW "FileSystemWather"를 호출하는 스레드를 디버깅하고 감시하는 동안 당신은 주목 했습니까? 어쨌든 날씨가 뭐야?
devlord

FileSystemWatcher가있는 Windows 서비스가 프로세스가 파일을 닫기 전에 파일을 읽으려고하기 때문에 정확하게이 문제가 발생했습니다.
freedeveloper

6

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

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);
}

3
에서 재귀에서 스택이 오버플로 될 때까지 몇 초 GetStreamAsync()입니까?
CAD bloke

@CADbloke, 당신은 아주 좋은 지적을 올렸습니다. 실제로 파일을 오랫동안 사용할 수없는 경우 샘플에 스택 오버플로 예외가있을 수 있습니다. 이 답변과 관련하여 stackoverflow.com/questions/4513438/… , 5 시간 후에 예외가 발생할 수 있습니다.
Ivan Branets

유스 케이스와 관련하여 파일 읽기 시도가 10 번 실패했다고 가정하면 I / O 예외를 처리하는 것이 좋습니다. 다른 전략은 10 번의 시도가 실패한 후 1 초 동안 대기 시간을 늘리는 것일 수 있습니다. 두 가지를 혼합하여 사용할 수도 있습니다.
Ivan Branets

파일이 잠겨있는 사용자에게 경고합니다. 그들은 일반적으로 그것을 잠그기 때문에 아마도 그것에 대해 무언가를 할 것입니다. 아님
CAD bloke

파일이 아직 준비되지 않았기 때문에 경우에 따라 재시도 정책을 사용해야합니다. 일부 임시 폴더에 이미지를 다운로드하는 데스크탑 응용 프로그램을 상상해보십시오. 응용 프로그램이 다운로드를 시작하는 동시에 파일 탐색기에서이 폴더를 엽니 다. Windows는 축소판을 즉시 만들고 파일을 잠그려고합니다. 동시에 앱은 잠긴 이미지를 다른 위치로 바꾸려고 시도합니다. 재시도 정책을 사용하지 않으면 예외가 발생합니다.
Ivan Branets

4

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

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


1
파일을 먼저 열지 않으면이 API를 사용할 수 없으므로 더 이상 필요하지 않습니다.
Harry Johnston

1
슈뢰딩거의 파일.
TheLastGIS

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

도움이 되었기를 바랍니다!


10
실제로 수행하는 점검은 괜찮습니다. 함수 안에 넣는 것은 잘못된 것입니다. 파일을 열기 전에 이와 같은 기능을 사용하고 싶지 않습니다. 함수 내에서 파일이 열리고 확인되고 닫힙니다. 그런 다음 프로그래머는 파일을 여전히 사용한다고 가정하고 사용하기 위해 파일을 열려고 시도합니다. 이 파일을 열기 위해 대기 한 다른 프로세스에서 사용하고 잠글 수 있기 때문에 이것은 나쁩니다. 첫 번째로 열린 시간 (확인)과 두 번째로 열린 시간 (사용) 사이에 OS가 프로세스를 예약 해제하고 다른 프로세스를 실행 중일 수 있습니다.
Lakey

3

위의 허용 된 대답은 FileShare.Read 모드로 쓰기 위해 파일을 열었거나 파일에 읽기 전용 속성이있는 경우 코드가 작동하지 않는 문제가 있습니다. 이 수정 된 솔루션은 다음 두 가지 사항을 명심해야합니다 (허용 된 솔루션의 경우도 마찬가지).

  1. 쓰기 공유 모드로 열린 파일에는 작동하지 않습니다
  2. 스레딩 문제는 고려하지 않으므로이를 잠 그거나 스레딩 문제를 별도로 처리해야합니다.

위의 사항을 염두에두면 파일 쓰기잠겨 있는지 또는 읽기를 방지하기 위해 잠겨 있는지 확인합니다 .

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;
}

여전히 허용되는 답변과 동일한 문제가 있습니다. 파일이 특정 프로세스 의 특정 순간에 다른 프로세스 의해 잠겨 있는지 여부 만 알려줍니다 . 이는 유용한 정보가 아닙니다. 함수가 반환 될 때까지 결과가 이미 오래되었을 수 있습니다!
Harry Johnston

1
즉, 주어진 순간에 만 확인하거나 이벤트에 가입 할 수 있습니다. 허용 된 솔루션에 비해이 방법의 장점은 읽기 전용 특성과 쓰기 잠금을 확인하고 오 탐지를 반환 할 수 없다는 것입니다.
rboy

3

3 줄짜리 작업을 제외하고 참조 용으로 : 완전한 정보 를 원한다면 Microsoft Dev Center에 약간의 프로젝트가 있습니다.

https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4

소개에서 :

.NET Framework 4.0에서 개발 된 C # 샘플 코드는 파일을 잠그는 프로세스를 찾는 데 도움이됩니다. rstrtmgr.dll 에 포함 된 RmStartSession 함수는 재시작 관리자 세션을 작성하는 데 사용되었으며 리턴 결과에 따라 Win32Exception 오브젝트의 새 인스턴스가 작성됩니다. RmRegisterRescources 함수 를 통해 자원을 다시 시작 관리자 세션에 등록한 후 RmGetList 함수가 호출되어 RM_PROCESS_INFO 배열 을 열거하여 응용 프로그램이 특정 파일을 사용 중인지 확인합니다 .

"재시작 관리자 세션"에 연결하여 작동합니다.

재시작 관리자는 세션에 등록 된 자원 목록을 사용하여 종료 및 재시작해야하는 응용 프로그램 및 서비스를 판별합니다. 리소스는 파일 이름, 서비스 짧은 이름 또는 실행중인 응용 프로그램을 설명하는 RM_UNIQUE_PROCESS 구조로 식별 할 수 있습니다 .

그것은 당신의 특정 요구에 대해 약간 과도하게 설계 되었을 수도 있습니다 ... 그러나 그것이 당신이 원하는 것이라면 , 계속 진행하여 vs 프로젝트를 잡으십시오.


2

내 경험상 일반적 으로이 작업을 수행 한 다음 파일을 '보호'하여 멋진 것을 수행하고 '보호 된'파일을 사용하려고합니다. 이와 같이 사용하려는 파일이 하나만 있으면 Jeremy Thompson의 답변에 설명 된 트릭을 사용할 수 있습니다. 그러나 설치 프로그램을 작성하는 등의 많은 파일에서이 작업을 수행하려고하면 약간의 피해를 입게됩니다.

이 문제를 해결할 수있는 매우 우아한 방법은 파일 시스템에서 폴더 이름을 사용하는 경우 파일 시스템에서 폴더 이름을 변경할 수 없다는 사실을 사용하는 것입니다. 폴더를 동일한 파일 시스템에 보관하면 매력처럼 작동합니다.

이것이 악용 될 수있는 명백한 방법을 알고 있어야합니다. 결국 파일이 잠기지 않습니다. 또한 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);
}

개별 파일의 경우 Jeremy Thompson이 게시 한 잠금 제안을 고수합니다.


안녕하세요, 답변 변경의 순서가 당신이하는 포스트 명확히 할 수 있기 때문에 위의 이 인기 QA의 독자 의미 당신. 감사.
Jeremy Thompson

1
감사합니다. 게시물을 수정하겠습니다. 주로 FileShare잠금을 올바르게 사용 하고 확인하기 때문에 귀하의 솔루션을 사용합니다 .
atlaste

2

가장 잘 알 수있는 한 허용되는 답변과 동일한 코드이지만 코드는 적습니다.

    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<FileStream> 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);
    }

2

내 라이브러리를 사용하여 여러 앱에서 파일에 액세스 할 수 있습니다.

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를 반환합니다. 그러나 파일을 업로드하는 앱은 파일 잠금에서도 수행해야합니다. 객체에 접근 할 수없는 경우 metod는 false를 반환합니다.


1
도구 나 라이브러리를 답으로 게시하지 마십시오. 적어도 그것이 답변 자체에서 문제해결하는 방법을 보여 주십시오 .
paper1111

데모 추가 :) 죄송합니다 @ paper1111
Tomasz Żmuda

1
파일을 사용하는 모든 프로세스가 협력해야합니다. OPs 원래 문제에 적용되지는 않습니다.
Harry Johnston

2

한때 온라인 백업 아카이브에 PDF를 업로드해야했습니다. 그러나 사용자가 다른 프로그램 (예 : PDF 리더)에서 파일을 열면 백업이 실패합니다. 서두르 면서이 스레드에서 몇 가지 주요 답변을 시도했지만 작동하지 못했습니다. 나를 위해 일한 것은 PDF 파일을 자체 디렉토리 로 옮기려고했습니다 . 파일이 다른 프로그램에서 열려 있으면 이것이 실패하고 이동이 성공하면 별도의 디렉토리로 이동했을 때와 같이 복원 작업이 필요하지 않습니다. 다른 사람의 특정 사용 사례에 유용 할 수 있도록 기본 솔루션을 게시하고 싶습니다.

string str_path_and_name = str_path + '\\' + str_filename;
FileInfo fInfo = new FileInfo(str_path_and_name);
bool open_elsewhere = false;
try
{
    fInfo.MoveTo(str_path_and_name);
}
catch (Exception ex)
{
    open_elsewhere = true;
}

if (open_elsewhere)
{
    //handle case
}

0

이것이 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올바른 IOException을 잡을 수 있도록 블록에 약간의주의를 기울일 수 있습니다 . 이 파일들은 어쨌든 임시 파일이기 때문에 시작시 앱 저장소를 지울 것입니다.

파일이 사용 중인지 여부를 확인하는 OP의 질문 범위를 넘어서지 만 실제로 여기에 도착했을 때 해결하려는 문제 였으므로 다른 사람에게 유용 할 것입니다.


0

이 같은 도움이 되겠습니까?

var fileWasWrittenSuccessfully = false;
while (fileWasWrittenSuccessfully == false)
{
    try
    {
        lock (new Object())
        {
            using (StreamWriter streamWriter = new StreamWriter(filepath.txt"), true))
            {
                streamWriter.WriteLine("text");
            }
        }

        fileWasWrittenSuccessfully = true;
    }
    catch (Exception)
    {

    }
}

-2

임시 디렉토리로 파일을 이동 / 복사하십시오. 가능하면 잠금이 없으며 잠금을받지 않고 임시 디렉토리에서 안전하게 작업 할 수 있습니다. 그렇지 않으면 x 초 안에 다시 이동하십시오.


@jcolebrand는 무엇을 잠급니까? 당신이 복사 한 것? 아니면 당신이 임시 디렉토리에 넣은 것입니까?
Cullub

5
다른 사람이 작업하지 않을 것으로 예상하고 파일을 복사하고 임시 파일을 사용하려고하면 파일을 복사 한 직후 누군가가 파일을 잠그면 데이터가 손실 될 수 있습니다.
jcolebrand

-3

이 해결 방법을 사용하지만 IsFileLocked 함수로 파일 잠금을 확인할 때와 파일을 열 때 사이의 시간 간격이 있습니다. 이 시간 범위에서 다른 스레드가 파일을 열 수 있으므로 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 함수를 전혀 사용할 필요가 없었습니까?

감사


4
IsFileBeingUsed 란 무엇입니까? IsFileBeingUsed에 대한 소스 코드?
Kiquenet
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.