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?
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?
Câu trả lời:
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 .
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;
}
using
sẽ phải sử dụng ...
using
sẽ không hoạt động ở đây. Khi kết thúc khối đang sử dụng, fs
sẽ 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)!
Đâ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.
Đầ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.
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;
}
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);
}
}
}
attempts
thông qua bởi ref? Điều đó không có ý nghĩa. Không thử nghiệm <=
thay vì chỉ ==
.
throw ex
thực sự là điều đúng đắn phải làm.