làm thế nào bạn có thể dễ dàng kiểm tra xem quyền truy cập có bị từ chối đối với tệp trong .NET hay không?


100

Về cơ bản, tôi muốn kiểm tra xem tôi có quyền mở tệp hay không trước khi thực sự cố gắng mở nó; Tôi không muốn sử dụng thử / bắt cho séc này trừ khi tôi phải làm như vậy. Có thuộc tính truy cập tệp nào tôi có thể kiểm tra trước khi sử dụng không?


2
Chú thích khi tôi thay đổi thẻ: "tôi đang sửa chữa". Không phải trò đùa.
Joel Coehoorn

6
Đồng ý- Tôi ước có một TryOpen (tức là mẫu Thử-Phân tích cú pháp).
Tristan

Câu trả lời:


157

Tôi đã làm điều này không biết bao nhiêu lần trong quá khứ, và gần như lần nào tôi làm điều đó, tôi thậm chí đã sai khi cố gắng.

Quyền đối với tệp (thậm chí cả sự tồn tại của tệp) rất dễ thay đổi - chúng có thể thay đổi bất kỳ lúc nào. Nhờ Định luật Murphy, điều này đặc biệt bao gồm khoảng thời gian ngắn giữa khi bạn kiểm tra tệp và khi bạn cố gắng mở nó. Một sự thay đổi thậm chí có nhiều khả năng xảy ra nếu bạn đang ở trong một khu vực mà bạn biết rằng bạn cần phải kiểm tra trước. Tuy nhiên, kỳ lạ là nó sẽ không bao giờ xảy ra trong môi trường thử nghiệm hoặc phát triển của bạn, những môi trường có xu hướng khá tĩnh. Điều này khiến vấn đề khó theo dõi sau này và dễ khiến loại lỗi này đưa vào sản xuất.

Điều này có nghĩa là bạn vẫn phải có khả năng xử lý ngoại lệ nếu quyền hoặc sự tồn tại của tệp là không tốt, mặc dù bạn đã kiểm tra. Mã xử lý ngoại lệ là bắt buộc , cho dù bạn có kiểm tra trước các quyền của tệp hay không. Mã xử lý ngoại lệ cung cấp tất cả các chức năng của sự tồn tại hoặc kiểm tra quyền. Ngoài ra, trong khi các trình xử lý ngoại lệ như thế này được biết là chậm, điều quan trọng cần nhớ là i / o đĩa thậm chí còn chậm hơn ... chậm hơn rất nhiều ... và việc gọi hàm .Exists () hoặc kiểm tra quyền sẽ buộc phải thực hiện thêm một chuyến ra khỏi hệ thống tệp.

Tóm lại, việc kiểm tra ban đầu trước khi thử mở tệp vừa thừa vừa lãng phí. Không có lợi ích bổ sung nào đối với việc xử lý ngoại lệ, nó thực sự sẽ gây hại chứ không giúp ích gì cho hiệu suất của bạn, nó làm tăng thêm chi phí về nhiều mã phải được duy trì và nó có thể tạo ra các lỗi nhỏ vào mã của bạn. Việc thực hiện kiểm tra ban đầu không có gì thuận lợi cả. Thay vào đó, điều chính xác ở đây là chỉ cố gắng mở tệp và nỗ lực của bạn vào một trình xử lý ngoại lệ tốt nếu nó không thành công. Điều này cũng đúng ngay cả khi bạn chỉ đang kiểm tra xem tệp có tồn tại hay không. Lý do này áp dụng cho bất kỳ tài nguyên dễ bay hơi nào .


5
Chính xác. Đây là một ví dụ cổ điển về điều kiện chủng tộc.
Powerlord

3
korro: dù sao thì bạn cũng phải có khả năng xử lý các quyền không hợp lệ khi bị lỗi và điều đó làm cho việc kiểm tra ban đầu trở nên thừa thãi và lãng phí.
Joel Coehoorn

2
Kiểm tra ban đầu có thể giúp xử lý các lỗi cụ thể phổ biến một cách dễ dàng - nhìn về phía trước thường dễ dàng hơn việc so khớp các thuộc tính ngoại lệ cụ thể với các nguyên nhân cụ thể. Việc thử / bắt vẫn là bắt buộc.
peterchen

5
Câu trả lời này không trả lời câu hỏi "làm thế nào để kiểm tra xem tôi có quyền mở tệp hay không" tại một số trường hợp trước khi cố gắng mở nó. Trường hợp rất có thể xảy ra, nếu quyền không được cho phép tại trường hợp đó, phần mềm sẽ không cố đọc tệp, ngay cả khi quyền rất có thể được cấp ngay sau khi quyền được kiểm tra.
Triynko

5
Không quan trọng liệu các quyền có dễ thay đổi hay không, khi bạn chỉ quan tâm đến chúng là gì tại thời điểm đó. Lỗi phải luôn được xử lý, nhưng nếu bạn kiểm tra quyền đọc mà nó không có ở đó, thì bạn có thể bỏ qua việc đọc tệp, ngay cả khi bạn có thể có quyền truy cập một giây sau đó. Bạn phải vẽ đường ở đâu đó.
Triynko

25

Mẹo nhanh cho bất kỳ ai khác đến đây gặp sự cố tương tự:

Để ý các ứng dụng đồng bộ hóa web như DropBox. Tôi vừa dành 2 giờ để nghĩ rằng câu lệnh "using" (Dispose pattern) bị hỏng trong .NET.

Cuối cùng tôi nhận ra rằng Dropbox liên tục đọc và ghi các tệp trong nền để đồng bộ hóa chúng.

Đoán xem thư mục Visual Studio Projects của tôi nằm ở đâu? Tất nhiên là bên trong thư mục "My Dropbox".

Do đó, khi tôi chạy ứng dụng của mình ở chế độ Gỡ lỗi, các tệp mà nó đang đọc và ghi cũng liên tục được DropBox truy cập để được đồng bộ hóa với máy chủ DropBox. Điều này gây ra xung đột khóa / truy cập.

Vì vậy, ít nhất bây giờ tôi biết rằng tôi cần một chức năng File Open mạnh mẽ hơn (tức là TryOpen () sẽ thực hiện nhiều lần). Tôi ngạc nhiên vì nó chưa phải là một phần tích hợp của khuôn khổ.

[Cập nhật]

Đây là chức năng trợ giúp của tôi:

/// <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 Tôi nghĩ bạn đã không đọc câu hỏi đúng cách HE muốn tránh thử bắt.
Ravisha

10
@Ravisha, bạn thậm chí đã đọc câu trả lời được bình chọn nhiều nhất của Joel? Như Joel nói, "Thay vào đó , những gì bạn làm chỉ là cố gắng mở tệp và xử lý ngoại lệ nếu nó không thành công" . Xin đừng downvote chỉ vì bạn không thích thực tế là không thể tránh khỏi một điều gì đó.
Ash

Cảm ơn vì mã! Một điều, có thể tốt hơn nếu sử dụng bằng cách sử dụng, ví dụ: xem câu trả lời của Tazeem ở đây
Cel

Cho dù bạn trả lại luồng phim thì người gọi usingsẽ phải sử dụng ...
Cel 24/10/11

@Cel - usingsẽ không hoạt động ở đây. Khi kết thúc khối đang sử dụng, fssẽ bị đóng lại. Bạn sẽ cung cấp cho người gọi một luồng phim ĐÃ ĐÓNG CỬA (quá vô dụng)!
ToolmakerSteve

4

Đây là giải pháp bạn đang tìm kiếm

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

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

điều này tạo ra một quyền đọc mới dựa trên chế độ xem cho đường dẫn của tất cả các tệp, sau đó kiểm tra xem nó có ngang bằng với quyền truy cập tệp được đọc hay không.


3

Đầu tiên, những gì Joel Coehoorn nói.

Ngoài ra: bạn nên kiểm tra các giả định mà bạn mong muốn để tránh sử dụng try / catch trừ khi bạn phải làm như vậy. Lý do điển hình để tránh logic phụ thuộc vào các ngoại lệ (tạo Exceptionđối tượng hoạt động kém) có thể không liên quan đến mã đang mở tệp.

Tôi cho rằng nếu bạn đang viết một phương thức điền a List<FileStream>bằng cách mở mọi tệp trong một cây con thư mục và bạn dự kiến ​​rằng số lượng lớn chúng không thể truy cập được, bạn có thể muốn kiểm tra quyền đối với tệp trước khi cố gắng mở tệp để không nhận được quá nhiều ngoại lệ. Nhưng bạn vẫn xử lý ngoại lệ. Ngoài ra, có lẽ có điều gì đó rất sai trong thiết kế chương trình của bạn nếu bạn đang viết một phương thức thực hiện điều này.


-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: sử dụng "ném;" không phải "ném trái phépAccessException;". Bạn đang mất dấu vết ngăn xếp của mình.
John Saunders

Tại sao lại được attemptsthông qua bởi ref? Điều đó không có ý nghĩa. Không thử nghiệm <=thay vì chỉ ==.
Konrad Rudolph

1
@ John: tốt, trong trường hợp này đó là mong muốn để mất (lồng sâu) stack trace của cuộc gọi đệ quy vì vậy tôi nghĩ rằng trong trường hợp này throw exthực sự là điều đúng đắn phải làm.
Konrad Rudolph

2
@Konrad: @Rudzitis: Tôi đang thay đổi lý do của mình cho -1. Nó còn tệ hơn việc vặn ngăn xếp bằng cách "ném ex". Bạn đang vặn xoắn ngăn xếp bằng cách tạo ra một cách giả tạo các mức ngăn xếp bổ sung thông qua đệ quy vào thời điểm mà độ sâu ngăn xếp thực sự quan trọng. Đây là một bài toán lặp, không phải là một bài toán đệ quy.
John Saunders
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.