2012-09-04 24 views
9

Khi tệp đang được sao chép vào thư mục trình theo dõi tệp, làm thế nào tôi có thể xác định liệu tệp đó có được sao chép hoàn toàn và sẵn sàng để sử dụng không? Bởi vì tôi đang nhận được nhiều sự kiện trong quá trình sao chép tệp. (Tệp được sao chép qua một chương trình khác bằng File.Copy.)FileSystemWatcher - là Tập tin đã sẵn sàng để sử dụng

Trả lời

2

Tôi gặp sự cố này khi viết tệp. Tôi đã nhận được sự kiện trước khi tệp được viết hoàn toàn và đóng.

Giải pháp là sử dụng tên tệp tạm thời và đổi tên tệp sau khi hoàn tất. Sau đó, xem sự kiện đổi tên tệp thay vì tạo tệp hoặc thay đổi sự kiện.

+1

Điều này không có tác dụng nếu ai đó đang đẩy tệp cho bạn (như qua FTP hoặc thứ gì đó tương tự) - bạn phải biết tệp đã sẵn sàng sử dụng trước khi bạn có thể đổi tên và nói rằng nó sẵn sàng sử dụng :) – Tacroy

1

Lưu ý: vấn đề này không thể giải quyết được trong trường hợp chung. Nếu không có kiến ​​thức trước về việc sử dụng tệp, bạn không thể biết liệu các chương trình khác có hoàn thành thao tác với tệp hay không.

Trong trường hợp cụ thể của bạn, bạn sẽ có thể tìm ra những hoạt động File.Copy bao gồm.

Tệp đích có khả năng nhất bị khóa trong toàn bộ thao tác. Trong trường hợp này, bạn có thể chỉ cần thử mở tệp và xử lý ngoại lệ "vi phạm chế độ chia sẻ".

Bạn cũng có thể đợi một thời gian ... - tùy chọn không đáng tin cậy, nhưng nếu bạn biết phạm vi kích thước của tệp, bạn có thể có sự chậm trễ hợp lý để cho phép Sao chép hoàn tất.

Bạn cũng có thể "phát minh" một số loại hệ thống giao dịch - tức là tạo một tệp khác như "destination_file_name.COPYLOCK" chương trình sao chép tệp sẽ tạo trước khi sao chép "destination_file_name" và xóa sau đó.

9

Khi tôi gặp sự cố này, giải pháp tốt nhất mà tôi đưa ra là tiếp tục cố gắng lấy khóa độc quyền trên tệp; trong khi tệp đang được viết, nỗ lực khóa sẽ không thành công, về cơ bản là phương thức trong câu trả lời this. Một khi tập tin không được ghi vào nữa, khóa sẽ thành công.

Thật không may, cách duy nhất để làm điều đó là để quấn thử/nắm bắt xung quanh việc mở tệp, điều này khiến tôi khó chịu - phải sử dụng try/catch luôn gây đau đớn. Mặc dù vậy, dường như không có cách nào xung quanh điều đó, vì vậy đó là những gì tôi đã sử dụng.

Sửa đổi mã trong câu trả lời mà không lừa, vì vậy tôi đã kết thúc bằng một cái gì đó như thế này:

private void WaitForFile(FileInfo file) 
{ 
    FileStream stream = null; 
    bool FileReady = false; 
    while(!FileReady) 
    { 
     try 
     { 
      using(stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)) 
      { 
       FileReady = true; 
      } 
     } 
     catch (IOException) 
     { 
      //File isn't ready yet, so we need to keep on waiting until it is. 
     } 
     //We'll want to wait a bit between polls, if the file isn't ready. 
     if(!FileReady) Thread.Sleep(1000); 
    } 
} 
+3

Bạn có thể muốn thêm một số và bỏ sau khi thử không thành công. – McGarnagle

1

Đây là một phương pháp mà sẽ thử lại quyền truy cập tập tin lên đến X số lần, với một Sleep giữa cố gắng. Nếu nó không bao giờ được tiếp cận, ứng dụng di chuyển trên:

private static bool GetIdleFile(string path) 
{ 
    var fileIdle = false; 
    const int MaximumAttemptsAllowed = 30; 
    var attemptsMade = 0; 

    while (!fileIdle && attemptsMade <= MaximumAttemptsAllowed) 
    { 
     try 
     { 
      using (File.Open(path, FileMode.Open, FileAccess.ReadWrite)) 
      { 
       fileIdle = true; 
      } 
     } 
     catch 
     { 
      attemptsMade++; 
      Thread.Sleep(100); 
     } 
    } 

    return fileIdle; 
} 

Nó có thể được sử dụng như thế này:

private void WatcherOnCreated(object sender, FileSystemEventArgs e) 
{ 
    if (GetIdleFile(e.FullPath)) 
    { 
     // Do something like... 
     foreach (var line in File.ReadAllLines(e.FullPath)) 
     { 
      // Do more... 
     } 
    } 
} 
0
private Stream ReadWhenAvailable(FileInfo finfo, TimeSpan? ts = null) => Task.Run(() => 
    { 
     ts = ts == null ? new TimeSpan(long.MaxValue) : ts; 
     var start = DateTime.Now; 
     while (DateTime.Now - start < ts) 
     { 
      Thread.Sleep(200); 
      try 
      { 
       return new FileStream(finfo.FullName, FileMode.Open); 
      } 
      catch { } 
     } 
     return null; 
    }) 
    .Result; 

... tất nhiên, bạn có thể sửa đổi các khía cạnh của việc này cho phù hợp với bạn nhu cầu.