2011-01-13 39 views
25

Tôi có một chương trình chạy dưới dạng Dịch vụ Windows; nó xử lý các tệp trong một thư mục cụ thể. Vì nó là một dịch vụ, nó liên tục theo dõi một thư mục cho các tệp mới đã được thêm vào. Một phần công việc của chương trình là thực hiện so sánh các tệp trong thư mục đích và gắn cờ các tệp không phù hợp. Những gì tôi muốn làm là có thể phát hiện nếu một hoạt động sao chép đang được tiến hành và khi nó đã hoàn thành, do đó theo cách đó một tập tin không bị gắn cờ sớm nếu nó khớp với tập tin chưa được sao chép sang thư mục đích.C# - Đang đợi thao tác sao chép hoàn tất

Điều tôi đã nghĩ đến khi làm là sử dụng FileSystemWatcher để xem thư mục đích và xem hoạt động sao chép có đang diễn ra hay không. Nếu có, tôi đưa chủ đề chính của chương trình vào trạng thái ngủ cho đến khi thao tác sao chép hoàn tất, sau đó tiếp tục thực hiện thao tác trên thư mục như bình thường. Tôi chỉ muốn có được một số hiểu biết về cách tiếp cận này và xem nó có hợp lệ hay không; nếu bất cứ ai khác có cách tiếp cận độc đáo khác cho vấn đề này, nó sẽ được đánh giá cao.

UPDATE:

Cảm ơn tất cả các bạn cho ý kiến ​​của bạn

UPDATE 2:

tôi xin lỗi vì sự nhầm lẫn, khi tôi nói mục đích, ý tôi là thư mục nguồn chứa tất cả các file tôi muốn để xử lý. Một phần chức năng của chương trình của tôi là sao chép cấu trúc thư mục của thư mục nguồn vào thư mục đích và sao chép tất cả các tệp hợp lệ vào thư mục đích đó, giữ nguyên cấu trúc thư mục của thư mục nguồn gốc, tức là người dùng có thể sao chép các thư mục chứa tệp vào thư mục nguồn. Tôi muốn ngăn chặn lỗi bằng cách đảm bảo rằng nếu một tập hợp thư mục mới chứa nhiều thư mục con và tệp được sao chép vào thư mục nguồn để xử lý, chương trình của tôi sẽ không bắt đầu hoạt động trên thư mục đích cho đến khi quá trình sao chép hoàn tất.

+0

+1. Đó là một câu hỏi rất hay. Tôi chưa nghĩ ra cách tiếp cận mà không có cảm giác như là hack. – David

+1

Câu hỏi này tương tự và có một số câu trả lời hay: http://stackoverflow.com/questions/30074/monitoring-files-how-to-know-when-a-file-is-complete – mfdoran

Trả lời

2

Điều bạn đang tìm kiếm là một trường hợp điển hình producer/consumer. Những gì bạn cần làm được nêu trong phần 'Producer/consumer queue' trên số page này. Điều này sẽ cho phép bạn sử dụng đa luồng (có thể kéo dài một backgroundworker) để sao chép các tập tin, do đó bạn không chặn luồng dịch vụ chính khỏi nghe sự kiện hệ thống & bạn có thể thực hiện các tác vụ có ý nghĩa hơn ở đó - như kiểm tra các tệp mới & cập nhật hàng đợi . Vì vậy, on main thread do check for new files trên background threads perform the actual coping task. Từ kinh nghiệm cá nhân (đã thực hiện nhiệm vụ này) không có quá nhiều hiệu năng từ phương pháp này trừ khi bạn đang chạy trên nhiều máy CPU nhưng quá trình này rất sạch sẽ & mịn + mã được tách biệt một cách hợp lý.

Nói tóm lại, những gì bạn phải làm là có một đối tượng như sau:

public class File 
{ 
    public string FullPath {get; internal set;} 
    public bool CopyInProgress {get; set;} // property to make sure 
    // .. other properties if desired 
} 

Sau đó, sau khi hướng dẫn đăng trên vấn đề một khóa trên các đối tượng tập tin & hàng đợi để cập nhật nó & sao chép nó. Sử dụng phương pháp này bạn có thể sử dụng type approaches này thay vì giám sát liên tục để hoàn tất sao chép tệp. Điểm quan trọng để nhận ra ở đây là dịch vụ của bạn chỉ có một thể hiện đối tượng Tệp trên mỗi tệp vật lý thực - chỉ cần đảm bảo bạn (1) khóa hàng đợi của bạn khi thêm & xóa & (2) khóa đối tượng Tệp thực tế khi khởi tạo bản cập nhật .

EDIT: Trên nơi tôi nói "không có quá nhiều đạt được hiệu suất từ ​​phương pháp này trừ" Tôi refere để nếu bạn làm phương pháp này trong một chủ đề duy nhất so sánh với @ Jason gợi ý phương pháp này phải được chú ý nhanh hơn do @ Jason của giải pháp thực hiện các hoạt động IO rất tốn kém mà sẽ thất bại trên hầu hết các trường hợp. Điều này tôi đã không được thử nghiệm nhưng tôi khá chắc chắn như cách tiếp cận của tôi không yêu cầu hoạt động IO mở (một lần duy nhất), dòng (một lần duy nhất) & đóng tập tin (một lần duy nhất). Cách tiếp cận @Jason gợi ý nhiều hoạt động mở, mở, mở, mở sẽ tất cả không thành công trừ trường hợp cuối cùng.

+0

Đây là những gì tôi muốn hoàn thành, tôi cần phải ngăn chương trình của tôi làm bất cứ điều gì với các tệp/thư mục trong thư mục nguồn trong khi bản sao đang ở phát triển. – kingrichard2005

10

Yup, sử dụng FileSystemWatcher nhưng thay vì xem sự kiện đã tạo, hãy xem sự kiện đã thay đổi. Sau mỗi lần kích hoạt, hãy thử mở tệp. Một cái gì đó như thế này:

var watcher = new FileSystemWatcher(path, filter); 
watcher.Changed += (sender, e) => { 
    FileStream file = null; 
    try { 
     Thread.Sleep(100); // hack for timing issues 
     file = File.Open(
      e.FullPath, 
      FileMode.Open, 
      FileAccess.Read, 
      FileShare.Read 
     ); 
    } 
    catch(IOException) { 
     // we couldn't open the file 
     // this is probably because the copy operation is not done 
     // just swallow the exception 
     return; 
    } 

    // now we have a handle to the file 
}; 

Đây là điều tuyệt vời nhất bạn có thể làm. Không có cách nào để biết rằng tệp đã sẵn sàng để bạn sử dụng.

+0

Đây là cách tôi ' cũng đã xử lý nó. Nó luôn luôn cảm thấy "Hack-ish" với tôi, nhưng 1 bởi vì tôi không có gì tốt hơn. – David

+1

@ivo s: Không hiệu quả? Đắt? Ai quan tâm? Nó đang xảy ra trên một sợi nền và rất khó có thể là một nút cổ chai. Ugh. Ngoài ra, bạn đang giả định rằng anh ta có quyền kiểm soát quá trình sao chép. Lưu ý rằng đây không phải là câu hỏi ban đầu của anh ấy. – jason

+2

Nếu bạn cảm thấy mạo hiểm, bạn có thể tránh được phí tổn vì phải nắm bắt các ngoại lệ bằng cách sử dụng lệnh gọi p/invoke để mở tệp thay vì cuộc gọi .NET wrapper. Các cuộc gọi WIN32 cơ bản chỉ đơn giản là trả về một xử lý null cho thất bại (bạn nên sắp xếp cuộc gọi bằng cách sử dụng .NetFileHandle để xử lý thích hợp). Khi cuộc gọi cuối cùng thành công, bạn có thể tạo đối tượng FileStream từ SafeFileHandle. Viết trình bao bọc của riêng bạn cho lệnh gọi p/invoke để trả về một FileStream (hoặc null khi thất bại) và tránh sự cần thiết của try/catch. –

2

Một cách tiếp cận là cố gắng mở tệp và xem bạn có gặp lỗi hay không. Tệp sẽ bị khóa nếu tệp đang được sao chép. Thao tác này sẽ mở tệp ở chế độ được chia sẻ để nó sẽ xung đột với khóa ghi đã mở trên tệp:

using(System.IO.File.Open("file", FileMode.Open,FileAccess.Read, FileShare.Read)) {} 

Khác là kiểm tra kích thước tệp. Nó sẽ thay đổi theo thời gian nếu tập tin đang được sao chép vào.

Cũng có thể nhận danh sách tất cả các ứng dụng đã mở một tệp nhất định, nhưng tôi không biết API cho điều này.

1

Tôi biết đây là một câu hỏi cũ, nhưng đây là câu trả lời tôi xoay vòng sau khi tìm kiếm câu trả lời cho vấn đề này. Điều này đã được tinh chỉnh rất nhiều để loại bỏ một số các quyền sở hữu từ những gì tôi đã làm việc trên, do đó, điều này có thể không biên dịch trực tiếp, nhưng nó sẽ cung cấp cho bạn một ý tưởng. Điều này phù hợp với tôi:



void BlockingFileCopySync(FileInfo original, FileInfo copyPath) 
{ 
    bool ready = false; 

    FileSystemWatcher watcher = new FileSystemWatcher(); 
    watcher.NotifyFilter = NotifyFilters.LastWrite; 
    watcher.Path = copyPath.Directory.FullName; 
    watcher.Filter = "*" + copyPath.Extension; 
    watcher.EnableRaisingEvents = true; 

    bool fileReady = false; 
    bool firsttime = true; 
    DateTime previousLastWriteTime = new DateTime(); 

    // modify this as you think you need to... 
    int waitTimeMs = 100; 

    watcher.Changed += (sender, e) => 
    { 
     // Get the time the file was modified 
     // Check it again in 100 ms 
     // When it has gone a while without modification, it's done. 
     while (!fileReady) 
     { 
      // We need to initialize for the "first time", 
      // ie. when the file was just created. 
      // (Really, this could probably be initialized off the 
      // time of the copy now that I'm thinking of it.) 
      if (firsttime) 
      { 
       previousLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName); 
       firsttime = false; 
       System.Threading.Thread.Sleep(waitTimeMs); 
       continue; 
      } 

      DateTime currentLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName); 

      bool fileModified = (currentLastWriteTime != previousLastWriteTime); 

      if (fileModified) 
      { 
       previousLastWriteTime = currentLastWriteTime; 
       System.Threading.Thread.Sleep(waitTimeMs); 
       continue; 
      } 
      else 
      { 
       fileReady = true; 
       break; 
      } 
     } 
    }; 

    System.IO.File.Copy(original.FullName, copyPath.FullName, true); 

    // This guy here chills out until the filesystemwatcher 
    // tells him the file isn't being writen to anymore. 
    while (!fileReady) 
    { 
     System.Threading.Thread.Sleep(waitTimeMs); 
    } 
} 

+0

Đáng buồn là giải pháp này không hiệu quả với tôi. GetLastWriteTime không được cập nhật trong khi tệp đang được sao chép. Tôi đang sử dụng Windows 7, có thể hành vi khác nhau trên Windows Server, không chắc chắn. – KyleLib

+0

@KyleLib - Thật sao? Thú vị, tôi cũng đang sử dụng Windows 7. Hiếu kỳ. Không chắc phải làm gì. Tôi có thể phải phá vỡ này trở lại và mess với nó một chút. – trycatch

Các vấn đề liên quan