.NET의 파일에 대한 액세스가 거부되었는지 어떻게 쉽게 확인할 수 있습니까?


100

기본적으로 실제로 파일을 열기 전에 파일을 열 수있는 권한이 있는지 확인하고 싶습니다. 꼭 필요한 경우가 아니면이 수표에 try / catch를 사용하고 싶지 않습니다. 미리 확인할 수있는 파일 액세스 속성이 있습니까?


2
태그를 변경할 때의 캡션 : "im correcting". 농담이 아닙니다.
Joel Coehoorn

6
동의 함-TryOpen (예 : Try-Parse 패턴)이 있었으면합니다.
Tristan

답변:


157

나는 과거에이 일을 수없이 해왔고, 거의 매번 시도조차하는 것이 잘못되었습니다.

파일 권한 (파일 존재 여부 포함)은 휘발성 이며 언제든지 변경 될 수 있습니다. Murphy의 법칙 덕분에 특히 파일을 확인하고 파일을 열려고 할 때 사이의 짧은 기간 이 포함됩니다. 먼저 확인해야하는 영역에있는 경우 변경 가능성이 더 높습니다. 그러나 이상하게도 상당히 정적 인 경향이있는 테스트 또는 개발 환경에서는 절대 발생하지 않습니다. 이로 인해 나중에 문제를 추적하기가 어렵고 이러한 종류의 버그가 프로덕션에 쉽게 적용됩니다.

이것이 의미하는 바는 검사에도 불구하고 파일 권한이나 존재가 나쁜 경우 예외를 처리 할 수 ​​있어야한다는 것입니다. 사전에 파일 권한을 확인하든 안하든 예외 처리 코드가 필요 합니다. 예외 처리 코드는 존재 또는 권한 검사의 모든 기능을 제공 합니다. 또한 이와 같은 예외 처리기는 느린 것으로 알려져 있지만 디스크 I / o가 훨씬 더 느립니다 ... 훨씬 느리고 ... .Exists () 함수를 호출하거나 권한을 확인하면 추가 트립이 발생 한다는 점을 기억하는 것이 중요합니다. 파일 시스템 밖으로.

요약하면 파일을 열기 전에 초기 검사는 중복되고 낭비입니다. 예외 처리에 대한 추가 이점은 없습니다. 실제로 성능에 도움이되지 않고 손상을 입히고 유지 관리해야하는 더 많은 코드 측면에서 비용을 추가하고 코드에 미묘한 버그를 유발할 수 있습니다. 초기 확인을 수행하는 데는 전혀 이점이 없습니다. 대신, 여기서 올바른 것은 파일을 열고 실패 할 경우 좋은 예외 처리기에 노력을 기울이는 것입니다. 파일이 존재하는지 여부를 확인하는 경우에도 마찬가지입니다. 이 추론은 모든 휘발성 자원에 적용됩니다 .


5
바로 그거죠. 이것은 경쟁 조건의 전형적인 예입니다.
Powerlord

3
korro : 어쨌든 실패시 잘못된 권한을 처리 할 수 ​​있어야하므로 초기 검사가 중복되고 낭비됩니다.
Joel Coehoorn

2
초기 검사는 일반적인 특정 오류를 정상적으로 처리하는 데 도움이 될 수 있습니다. 특정 예외 속성을 특정 원인에 일치시키는 것보다 앞을 보는 것이 더 쉽습니다. try / catch는 여전히 의무적입니다.
peterchen

5
이 대답은 파일을 열기 전에 "파일을 열 수있는 권한이 있는지 확인하는 방법"이라는 질문에 대답하지 않습니다. 해당 인스턴스에서 권한이 허용되지 않으면 권한이 확인 된 직후에 권한이 부여 되더라도 소프트웨어가 파일 읽기를 시도하지 않을 수 있습니다.
Triynko 2011

5
그 순간에 그들이 무엇인지에만 관심을 가질 때 권한이 휘발성인지 여부는 중요하지 않습니다. 실패는 항상 처리되어야하지만 읽기 권한을 확인했지만 거기에없는 경우 잠시 후에 액세스 할 수있는 경우에도 파일 읽기를 건너 뛰는 것이 좋습니다. 어딘가에 선을 그려야합니다.
Triynko 2011

25

비슷한 문제로 여기에 오는 다른 사람을위한 빠른 팁 :

DropBox와 같은 웹 동기화 앱에주의하십시오. 방금 2 시간 동안 "using"문 (Dispose 패턴)이 .NET에서 손상되었다고 생각했습니다.

결국 Dropbox가 파일을 동기화하기 위해 백그라운드에서 지속적으로 파일을 읽고 쓰는 것을 깨달았습니다.

내 Visual Studio 프로젝트 폴더가 어디에 있는지 추측합니까? 물론 "내 Dropbox"폴더 안에 있습니다.

따라서 디버그 모드에서 응용 프로그램을 실행할 때 읽고 쓰는 파일도 DropBox 서버와 동기화되기 위해 계속해서 DropBox에 의해 액세스되었습니다. 이로 인해 잠금 / 액세스 충돌이 발생했습니다.

그래서 적어도 이제 더 강력한 파일 열기 함수 (예 : 여러 번 시도하는 TryOpen ())가 필요하다는 것을 알고 있습니다. 나는 그것이 이미 프레임 워크의 내장 된 부분이 아니라는 것에 놀랐다.

[최신 정보]

내 도우미 기능은 다음과 같습니다.

/// <summary>
/// Tries to open a file, with a user defined number of attempt and Sleep delay between attempts.
/// </summary>
/// <param name="filePath">The full file path to be opened</param>
/// <param name="fileMode">Required file mode enum value(see MSDN documentation)</param>
/// <param name="fileAccess">Required file access enum value(see MSDN documentation)</param>
/// <param name="fileShare">Required file share enum value(see MSDN documentation)</param>
/// <param name="maximumAttempts">The total number of attempts to make (multiply by attemptWaitMS for the maximum time the function with Try opening the file)</param>
/// <param name="attemptWaitMS">The delay in Milliseconds between each attempt.</param>
/// <returns>A valid FileStream object for the opened file, or null if the File could not be opened after the required attempts</returns>
public FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess,FileShare fileShare,int maximumAttempts,int attemptWaitMS)
{
    FileStream fs = null;
    int attempts = 0;

    // Loop allow multiple attempts
    while (true)
    {
        try
        {
            fs = File.Open(filePath, fileMode, fileAccess, fileShare);

            //If we get here, the File.Open succeeded, so break out of the loop and return the FileStream
            break;
        }
        catch (IOException ioEx)
        {
            // IOExcception is thrown if the file is in use by another process.

            // Check the numbere of attempts to ensure no infinite loop
            attempts++;
            if (attempts > maximumAttempts)
            {
                // Too many attempts,cannot Open File, break and return null 
                fs = null;
                break;
            }
            else
            {
                // Sleep before making another attempt
                Thread.Sleep(attemptWaitMS);

            }

        }

    }
    // Reutn the filestream, may be valid or null
    return fs;
}

3
@Ash 나는 u가 제대로 질문을 읽지 않았다고 생각합니다.
Ravisha 2010

10
@Ravisha, Joel의 최고 투표 답변도 읽었습니까? Joel이 말했듯이 "대신에 파일을 열고 실패 할 경우 예외를 처리하는 것 입니다. " 무언가를 피할 수 없다는 사실이 마음에 들지 않는다고해서 반대표를 던지지 마십시오.
애쉬

코드 감사합니다! 한 가지, 예를 들어 Tazeem 답변을
Cel

파일 스트림을 반환하면 using호출자가를 사용해야합니다 ...
Cel

@Cel- using여기서 작동하지 않습니다. 사용 블록이 끝나면 fs강제로 닫힙니다. 호출자에게 닫힌 (너무 쓸모없는) 파일 스트림을 제공합니다!
ToolmakerSteve

4

찾고있는 솔루션은 다음과 같습니다.

var fileIOPermission = new FileIOPermission(FileIOPermissionAccess.Read,
                                            System.Security.AccessControl.AccessControlActions.View,
                                            MyPath);

if (fileIOPermission.AllFiles == FileIOPermissionAccess.Read)
{
    // Do your thing here...
}

이것은 모든 파일의 경로에 대한보기를 기반으로 새로운 읽기 권한을 생성 한 다음 파일 액세스 읽기와 동일한 지 확인합니다.


3

첫째, Joel Coehoorn이 말한 것.

또한 : 꼭 필요한 경우가 아니라면 try / catch를 사용하지 않으려는 기본 가정을 조사해야합니다. 예외에 의존하는 논리를 피하는 일반적인 이유 ( Exception객체 생성 성능이 좋지 않음)는 파일을 여는 코드와 관련이 없을 것입니다.

List<FileStream>디렉토리 하위 트리의 모든 파일을 열어서 a를 채우는 메서드를 작성하고 있고 많은 수의 파일에 액세스 할 수 없을 것으로 예상했다면 파일을 열기 전에 파일 권한을 확인하여 너무 많은 예외가 발생합니다. 그러나 여전히 예외를 처리합니다. 또한이 작업을 수행하는 메서드를 작성하는 경우 프로그램 디자인에 심각한 문제가있을 수 있습니다.


-1
public static bool IsFileLocked(string filename)
        {
            bool Locked = false;
            try
            {
                FileStream fs =
                    File.Open(filename, FileMode.OpenOrCreate,
                    FileAccess.ReadWrite, FileShare.None);
                fs.Close();
            }
            catch (IOException ex)
            {
                Locked = true;
            }
            return Locked;
        }

-3
public static FileStream GetFileStream(String filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, ref int attempts, int attemptWaitInMilliseconds)
{            
    try
    {
         return File.Open(filePath, fileMode, fileAccess, fileShare);
    }
    catch (UnauthorizedAccessException unauthorizedAccessException)
    {
        if (attempts <= 0)
        {
            throw unauthorizedAccessException;
        }
        else
        {
            Thread.Sleep(attemptWaitInMilliseconds);
            attempts--;
            return GetFileStream(filePath, fileMode, fileAccess, fileShare, ref attempts, attemptWaitInMilliseconds);
        }
    }
}

8
-1 : "throw;"사용 "throw authorizedAccessException;"이 아닙니다. 스택 추적을 잃고 있습니다.
John Saunders

attempts심판에 의해 전달됩니까? 말이 안 돼. <=대신에 대한 테스트도 수행하지 않습니다 ==.
Konrad Rudolph

1
@John : 음,이 경우 재귀 호출의 (깊게 중첩 된) 스택 추적을 잃어 버리는 것이 바람직 하므로이 경우에는 throw ex실제로 해야 할 일이 옳다고 생각 합니다.
Konrad Rudolph

2
@Konrad : @Rudzitis : -1에 대한 이유를 변경하고 있습니다. "throw ex"로 스택을 망치는 것보다 더 나쁩니다. 스택 깊이가 실제로 중요한 시점에 재귀를 통해 추가 스택 레벨을 인위적으로 유도하여 스택을 망가 뜨리고 있습니다. 이것은 반복적 인 문제가 아니라 반복적 인 문제입니다.
John Saunders
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.