2012-06-11 37 views
46

Khi một tệp được tạo (FileSystemWatcher_Created) trong một thư mục, tôi sao chép tệp này sang một thư mục khác. Nhưng khi tôi tạo một tệp lớn (> 10MB) không thể sao chép tệp, vì nó bắt đầu sao chép, khi tệp chưa được tạo xong ...
Điều này gây ra Không thể sao chép tệp, vì nó được sử dụng bởi tệp khác xử lý để được nâng lên. ;?. (
Bất kỳ sự giúp đỡChờ cho đến khi tệp được viết hoàn toàn

class Program 
{ 
    static void Main(string[] args) 
    { 
     string path = @"D:\levan\FolderListenerTest\ListenedFolder"; 
     FileSystemWatcher listener; 
     listener = new FileSystemWatcher(path); 
     listener.Created += new FileSystemEventHandler(listener_Created); 
     listener.EnableRaisingEvents = true; 

     while (Console.ReadLine() != "exit") ; 
    } 

    public static void listener_Created(object sender, FileSystemEventArgs e) 
    { 
     Console.WriteLine 
       (
        "File Created:\n" 
        + "ChangeType: " + e.ChangeType 
        + "\nName: " + e.Name 
        + "\nFullPath: " + e.FullPath 
       ); 
     File.Copy(e.FullPath, @"D:\levan\FolderListenerTest\CopiedFilesFolder\" + e.Name); 
     Console.Read(); 
    } 
} 
+1

Tôi tìm thấy câu trả lời ở đây xin lỗi [Vấn đề này được giải quyết trên Stackoverflow] [1] [1] : http://stackoverflow.com/questions/1406808/wait-for-file-to-be-freed-by-process – levi

+1

bản sao có thể có của [Có cách nào để kiểm tra xem tệp có phải là tôi không n sử dụng?] (http://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use) –

Trả lời

32

Chỉ có cách giải quyết cho vấn đề bạn đang phải đối mặt.

Kiểm tra xem id tệp có đang được xử lý hay không trước khi bắt đầu quá trình sao chép. Bạn có thể gọi hàm sau cho đến khi bạn nhận được giá trị False.

Phương pháp 1, sao chép trực tiếp từ this answer:

private bool IsFileLocked(FileInfo file) 
{ 
    FileStream stream = null; 

    try 
    { 
     stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); 
    } 
    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; 
    } 
    finally 
    { 
     if (stream != null) 
      stream.Close(); 
    } 

    //file is not locked 
    return false; 
} 

2 Phương pháp:

const int ERROR_SHARING_VIOLATION = 32; 
const int ERROR_LOCK_VIOLATION = 33; 
private bool IsFileLocked(string file) 
{ 
    //check that problem is not in destination file 
    if (File.Exists(file) == true) 
    { 
     FileStream stream = null; 
     try 
     { 
      stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.None); 
     } 
     catch (Exception ex2) 
     { 
      //_log.WriteLog(ex2, "Error in checking whether file is locked " + file); 
      int errorCode = Marshal.GetHRForException(ex2) & ((1 << 16) - 1); 
      if ((ex2 is IOException) && (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION)) 
      { 
       return true; 
      } 
     } 
     finally 
     { 
      if (stream != null) 
       stream.Close(); 
     } 
    } 
    return false; 
} 
+40

Hmm, đây là trực tiếp từ: http://stackoverflow.com/a/937558/97000. – pyrocumulus

+1

Và bạn quên sao chép các giá trị không đổi ('ERROR_SHARING_VIOLATION' = 32,' ERROR_LOCK_VIOLATION' = 33) –

4

Bạn đang thực sự may mắn - chương trình bằng văn bản cho khóa tập tin nó, vì vậy bạn không thể mở nó Nếu nó đã không khóa nó, bạn đã có thể sao chép một

Khi bạn không thể truy cập tệp, bạn có thể giả sử tệp vẫn đang được sử dụng (tốt hơn - hãy thử mở tệp ở chế độ độc quyền và xem liệu người khác hiện có đang sử dụng hay không mở nó, thay vì đoán từ sự thất bại của File.Copy) Nếu tập tin bị khóa, bạn sẽ phải sao chép nó vào một thời điểm khác.Nếu nó không bị khóa, bạn có thể sao chép nó (có tiềm năng nhỏ cho một điều kiện chủng tộc tại đây)

Khi nào là 'thời gian khác'? Tôi không nhớ khi FileSystemWatcher gửi nhiều sự kiện cho mỗi tập tin - kiểm tra nó ra, nó có thể là đủ cho bạn chỉ đơn giản là bỏ qua sự kiện này và chờ đợi một số khác. Nếu không, bạn luôn có thể thiết lập thời gian và kiểm tra lại tệp trong 5 giây.

7

Từ tài liệu cho FileSystemWatcher:

Sự kiện OnCreated được nâng lên ngay sau khi một tập tin được tạo ra. Nếu tệp đang được sao chép hoặc chuyển vào thư mục đã xem, sự kiện OnCreated sẽ được nâng lên ngay lập tức, theo sau là một hoặc nhiều sự kiện OnChanged.

Vì vậy, nếu bản sao không thành công, (bắt ngoại lệ), thêm nó vào danh sách các tệp vẫn cần phải di chuyển và thử bản sao trong sự kiện OnChanged. Cuối cùng, nó sẽ hoạt động.

Giống như (không đầy đủ; bắt ngoại lệ cụ thể, khởi tạo các biến, vv):

public static void listener_Created(object sender, FileSystemEventArgs e) 
    { 
     Console.WriteLine 
       (
        "File Created:\n" 
        + "ChangeType: " + e.ChangeType 
        + "\nName: " + e.Name 
        + "\nFullPath: " + e.FullPath 
       ); 
     try { 
      File.Copy(e.FullPath, @"D:\levani\FolderListenerTest\CopiedFilesFolder\" + e.Name); 
     } 
     catch { 
      _waitingForClose.Add(e.FullPath); 
     } 
     Console.Read(); 
    } 

    public static void listener_Changed(object sender, FileSystemEventArgs e) 
    { 
     if (_waitingForClose.Contains(e.FullPath)) 
     { 
       try { 
        File.Copy(...); 
        _waitingForClose.Remove(e.FullPath); 
       } 
       catch {} 
     } 
    } 
+0

đó là cách khá tốt nhưng tôi cần nó cùng một lúc, vì vậy tôi đã đặt IsFileReady trong khi và nó hoạt động ngay bây giờ – levi

2

Vâng, bạn đã cung cấp cho các câu trả lời cho mình; bạn phải đợi để tạo tệp để hoàn tất. Một cách để thực hiện việc này là kiểm tra xem tệp có còn đang được sử dụng hay không. Bạn có thể tìm thấy ví dụ về điều này tại đây: Is there a way to check if a file is in use?

Lưu ý rằng bạn sẽ phải sửa đổi mã này để mã hoạt động trong tình huống của bạn.Bạn có thể muốn có một cái gì đó tương tự (giả):

public static void listener_Created() 
{ 
    while CheckFileInUse() 
     wait 1000 milliseconds 

    CopyFile() 
} 

Rõ ràng bạn nên bảo vệ mình khỏi vô hạn while chỉ trong trường hợp việc áp dụng chủ sở hữu không bao giờ ra mắt khóa. Ngoài ra, nó có thể là giá trị kiểm tra các sự kiện khác từ FileSystemWatcher bạn có thể đăng ký. Có thể có một sự kiện mà bạn có thể sử dụng để phá vỡ toàn bộ vấn đề này.

0

Bạn có thể sử dụng mã sau đây để kiểm tra xem tệp có thể được mở bằng quyền truy cập độc quyền không (nghĩa là nó không được mở bởi ứng dụng khác). Nếu tệp không được đóng, bạn có thể đợi một lát và kiểm tra lại cho đến khi tệp được đóng và bạn có thể sao chép tệp một cách an toàn.

Bạn vẫn nên kiểm tra xem File.Copy có bị lỗi không, bởi vì ứng dụng khác có thể mở tệp giữa thời điểm bạn kiểm tra tệp và thời điểm bạn sao chép tệp.

public static bool IsFileClosed(string filename) 
{ 
    try 
    { 
     using (var inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None)) 
     { 
      return true; 
     } 
    } 
    catch (IOException) 
    { 
     return false; 
    } 
} 
+0

Tại sao lại là downvote? – Michael

+0

Mã này trả về gì nếu bạn gặp phải một Ngoại lệ không phải là một IOException? –

+0

Nếu File.Open ném một ngoại lệ khác với IOException, phương thức này không trả về một giá trị. Trách nhiệm của người gọi là kiểm tra bất kỳ trường hợp không phải IOExceptions nào có thể được nêu ra: https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx – Michael

6

Đây là một chuỗi cũ nhưng tôi sẽ thêm một số thông tin cho người khác.

Tôi gặp phải sự cố tương tự với chương trình ghi tệp PDF, đôi khi mất 30 giây để hiển thị .. đó là khoảng thời gian mà lớp watcher_FileCreated của tôi chờ trước khi sao chép tệp.

Các tệp không bị khóa.

Trong trường hợp này, tôi đã kiểm tra kích thước của PDF và sau đó đợi 2 giây trước khi so sánh kích thước mới, nếu chúng không đồng đều, luồng sẽ ngủ trong 30 giây và thử lại.

-3

Tôi muốn thêm câu trả lời ở đây, vì điều này đã hiệu quả đối với tôi. Tôi đã sử dụng thời gian trễ, trong khi vòng lặp, tất cả mọi thứ tôi có thể nghĩ đến.

Tôi đã mở cửa sổ Windows Explorer của thư mục đầu ra. Tôi đóng nó lại, và mọi thứ hoạt động như một sự quyến rũ.

Tôi hy vọng điều này sẽ giúp ai đó.

+1

vì vậy giờ đây ứng dụng bị treo nếu người dùng có thư mục đó mở? – Epirocks

0

Khi tệp được ghi dưới dạng nhị phân (byte byte), tạo tệp FileStream và giải pháp trên Không hoạt động, vì tệp đã sẵn sàng và được ghi trong mỗi byte, vì vậy trong trường hợp này bạn cần giải pháp khác như sau: Thực hiện việc này khi tập tin được tạo ra hoặc bạn muốn bắt đầu xử lý trong hồ sơ

long fileSize = 0; 
currentFile = new FileInfo(path); 

while (fileSize < currentFile.Length)//check size is stable or increased 
{ 
    fileSize = currentFile.Length;//get current size 
    System.Threading.Thread.Sleep(500);//wait a moment for processing copy 
    currentFile.Refresh();//refresh length value 
} 

//Now file is ready for any process! 
1

Vì vậy, sau khi liếc nhanh chóng thông qua một số những điều này và khác những câu hỏi tương tự như tôi đi vào một cuộc rượt đuổi ngỗng vui vẻ chiều nay cố gắng để giải quyết một vấn đề với hai chương trình riêng biệt sử dụng một tệp dưới dạng phương thức đồng bộ hóa (và cũng lưu tệp). Một chút của một tình huống bất thường, nhưng nó chắc chắn nhấn mạnh cho tôi những vấn đề với 'kiểm tra nếu tập tin bị khóa, sau đó mở nó nếu nó không' tiếp cận.

Sự cố là: tệp có thể bị khóa bị khóa giữa thời gian bạn kiểm tra và thời gian bạn thực sự mở tệp. Nó thực sự khó khăn để theo dõi các không thường xuyên Không thể sao chép các tập tin, bởi vì nó được sử dụng bởi một quá trình khác lỗi nếu bạn không tìm kiếm nó quá.

Độ phân giải cơ bản là chỉ cố gắng mở tệp bên trong khối catch để nếu khóa của nó, bạn có thể thử lại. Bằng cách đó không có thời gian trôi qua giữa kiểm tra và mở, hệ điều hành hiện chúng cùng một lúc.

Mã ở đây sử dụng Tệp.Sao chép, nhưng nó hoạt động ngang ngửa với bất kỳ phương pháp tĩnh của lớp File: File.Open, File.ReadAllText, File.WriteAllText vv

/// <param name="timeout">how long to keep trying in milliseconds</param> 
static void safeCopy(string src, string dst, int timeout) 
{ 
    while (timeout > 0) 
    { 
     try 
     { 
      File.Copy(src, dst); 

      //don't forget to either return from the function or break out fo the while loop 
      break; 
     } 
     catch (IOException) 
     { 
      //you could do the sleep in here, but its probably a good idea to exit the error handler as soon as possible 
     } 
     Thread.Sleep(100); 

     //if its a very long wait this will acumulate very small errors. 
     //For most things it's probably fine, but if you need precision over a long time span, consider 
     // using some sort of timer or DateTime.Now as a better alternative 
     timeout -= 100; 
    } 
} 

Một lưu ý nhỏ trên parellelism: này là một phương thức đồng bộ, sẽ chặn luồng của nó cả trong khi đợi và trong khi làm việc trên luồng. Đây là cách tiếp cận đơn giản nhất, nhưng nếu tệp vẫn bị khóa trong một thời gian dài, chương trình của bạn có thể không phản hồi. Parellelism là một chủ đề quá lớn để đi vào chiều sâu ở đây, (và số cách bạn có thể thiết lập không đồng bộ đọc/ghi là loại vô lý) nhưng đây là một cách nó có thể được parellelized.

public class FileEx 
{ 
    public static async void CopyWaitAsync(string src, string dst, int timeout, Action doWhenDone) 
    { 
     while (timeout > 0) 
     { 
      try 
      { 
       File.Copy(src, dst); 
       doWhenDone(); 
       break; 
      } 
      catch (IOException) { } 

      await Task.Delay(100); 
      timeout -= 100; 
     } 
    } 

    public static async Task<string> ReadAllTextWaitAsync(string filePath, int timeout) 
    { 
     while (timeout > 0) 
     { 
      try { 
       return File.ReadAllText(filePath); 
      } 
      catch (IOException) { } 

      await Task.Delay(100); 
      timeout -= 100; 
     } 
     return ""; 
    } 

    public static async void WriteAllTextWaitAsync(string filePath, string contents, int timeout) 
    { 
     while (timeout > 0) 
     { 
      try 
      { 
       File.WriteAllText(filePath, contents); 
       return; 
      } 
      catch (IOException) { } 

      await Task.Delay(100); 
      timeout -= 100; 
     } 
    } 
} 

Và đây là cách nó có thể được sử dụng:

public static void Main() 
{ 
    test_FileEx(); 
    Console.WriteLine("Me First!"); 
}  

public static async void test_FileEx() 
{ 
    await Task.Delay(1); 

    //you can do this, but it gives a compiler warning because it can potentially return immediately without finishing the copy 
    //As a side note, if the file is not locked this will not return until the copy operation completes. Async functions run synchronously 
    //until the first 'await'. See the documentation for async: https://msdn.microsoft.com/en-us/library/hh156513.aspx 
    CopyWaitAsync("file1.txt", "file1.bat", 1000); 

    //this is the normal way of using this kind of async function. Execution of the following lines will always occur AFTER the copy finishes 
    await CopyWaitAsync("file1.txt", "file1.readme", 1000); 
    Console.WriteLine("file1.txt copied to file1.readme"); 

    //The following line doesn't cause a compiler error, but it doesn't make any sense either. 
    ReadAllTextWaitAsync("file1.readme", 1000); 

    //To get the return value of the function, you have to use this function with the await keyword 
    string text = await ReadAllTextWaitAsync("file1.readme", 1000); 
    Console.WriteLine("file1.readme says: " + text); 
} 

//Output: 
//Me First! 
//file1.txt copied to file1.readme 
//file1.readme says: Text to be duplicated! 
Các vấn đề liên quan