2012-01-22 29 views
24

Tôi đang làm việc trên phần mềm máy chủ định kỳ cần lưu dữ liệu vào đĩa. Tôi cần đảm bảo rằng tệp cũ bị ghi đè và tệp không thể bị hỏng (ví dụ: chỉ ghi đè một phần) trong trường hợp các trường hợp bất ngờ.Lưu tập tin đáng tin cậy (File.Replace) trong môi trường bận rộn

tôi đã áp dụng mô hình sau:

string tempFileName = Path.GetTempFileName(); 
// ...write out the data to temporary file... 
MoveOrReplaceFile(tempFileName, fileName); 

... nơi MoveOrReplaceFile là:

public static void MoveOrReplaceFile(string source, string destination) { 
    if (source == null) throw new ArgumentNullException("source"); 
    if (destination == null) throw new ArgumentNullException("destination"); 
    if (File.Exists(destination)) { 
     // File.Replace does not work across volumes 
     if (Path.GetPathRoot(Path.GetFullPath(source)) == Path.GetPathRoot(Path.GetFullPath(destination))) { 
      File.Replace(source, destination, null, true); 
     } else { 
      File.Copy(source, destination, true); 
     } 
    } else { 
     File.Move(source, destination); 
    } 
} 

này hoạt động tốt miễn là máy chủ có quyền truy cập độc quyền cho tập tin. Tuy nhiên, File.Replace có vẻ rất nhạy cảm với việc truy cập bên ngoài vào các tệp. Bất cứ khi nào phần mềm của tôi chạy trên hệ thống có chống vi-rút hoặc hệ thống sao lưu theo thời gian thực, các lỗi File.Replace ngẫu nhiên bắt đầu xuất hiện:

System.IO.IOException: Không thể xóa tệp cần thay thế.

Dưới đây là một số nguyên nhân có thể là tôi đã loại bỏ:

  • tập tin chưa được phát hành xử lý: using() đảm bảo rằng tất cả các xử lý tập tin được phát hành càng sớm càng tốt.
  • Sự cố luồng: khóa() bảo vệ tất cả quyền truy cập vào từng tệp.
  • Ổ đĩa khác nhau: Tệp.Replace() không thành công khi được sử dụng trên ổ đĩa. Phương pháp của tôi kiểm tra điều này và quay trở lại File.Copy().

Và đây là một số gợi ý mà tôi đã đi qua, và tại sao tôi không muốn sử dụng chúng:

  • Volume Shadow Copy Service: Đây chỉ hoạt động miễn là ba vấn đề phần mềm -party (màn hình sao lưu và chống virus, vv) cũng sử dụng VSS. Sử dụng VSS yêu cầu tấn P/Invoke và có các vấn đề cụ thể cho nền tảng.
  • Khóa tệp: Trong C#, việc khóa tệp yêu cầu duy trì mở tệp FileStream. Nó sẽ giữ phần mềm của bên thứ ba ra, nhưng 1) Tôi vẫn sẽ không thể thay thế tệp bằng File.Replace và 2) Như tôi đã đề cập ở trên, tôi muốn ghi vào tệp tạm thời trước để tránh tình cờ tham nhũng.

Tôi đánh giá cao bất kỳ đầu vào nào khi nhận tệp.Replace để làm việc mọi lúc hoặc, nói chung, lưu/ghi đè tệp trên đĩa một cách đáng tin cậy.

+1

Bạn có mong đợi 'MoveOrReplaceFile' được chạy đồng thời (có nghĩa là truy cập bởi nhiều chủ đề * ứng dụng * của bạn hoặc thậm chí từ nhiều phiên bản ứng dụng của bạn)? –

+1

Mã của riêng tôi không bao giờ gọi MoveOrReplaceFile đồng thời, nhưng có các quy trình khác ngoài tầm kiểm soát của tôi có thể tìm cách đọc tệp. Những truy cập đọc ngẫu nhiên này là nguyên nhân khiến File.Replace thất bại. – matvei

Trả lời

26

Bạn riêng của họ thực sự muốn sử dụng tham số thứ 3, tên tập tin sao lưu. Điều đó cho phép Windows chỉ cần đổi tên tệp ban đầu mà không phải xóa nó. Việc xóa sẽ thất bại nếu bất kỳ quá trình nào khác có mở tệp mà không xóa chia sẻ, đổi tên không bao giờ là vấn đề. Sau đó bạn có thể tự xóa nó sau cuộc gọi Replace() và bỏ qua một lỗi. Cũng xóa nó trước khi Replace() gọi để đổi tên sẽ không thất bại và bạn sẽ dọn dẹp thất bại trước đó nỗ lực. Vì vậy, khoảng:

string backup = destination + ".bak"; 
File.Delete(backup); 
File.Replace(source, destination, backup, true); 
try { 
    File.Delete(backup); 
} 
catch { 
    // optional: 
    filesToDeleteLater.Add(backup); 
} 
+0

Hans, bạn có thể hiển thị mã cho mẫu này không? Điều này sau đó có thể trở thành câu trả lời kinh điển cho câu hỏi này trong tương lai. –

+0

Đây chính xác là những gì tôi đang tìm kiếm! Cảm ơn rất nhiều. – matvei

+2

Điều này không chỉ chuyển sự cố xuống một cấp độ? Điều gì sẽ xảy ra nếu không thể xóa tệp sao lưu? – grantnz

1

Nếu phần mềm đang ghi vào phân vùng NTFS, hãy thử sử dụng Transactional NTFS. Bạn có thể sử dụng AlphFS cho trình bao bọc .NET. Đó có lẽ là cách đáng tin cậy nhất để ghi tệp và ngăn chặn tham nhũng.

+0

Chỉ hoạt động trên Vista và sau đó. – CodesInChaos

+1

'File.Replace()' dưới mono kỳ diệu làm những việc như sử dụng POSIX 'rename()' trên các hệ điều hành khác. Sẽ không di chuyển khỏi các lớp được cài sẵn làm phức tạp tính di động? Ngoài ra, nếu 'File.Replace()' thực sự làm việc như là tài liệu và đáng tin cậy, nó đã cung cấp chức năng cần thiết… – binki

2

Có một số phương pháp có thể, ở đây một số trong số họ:

  1. Sử dụng "khóa" tập tin - một tập tin tạm thời được tạo ra trước khi phẫu thuật và chỉ ra các tác giả khác (hoặc đọc) mà các tập tin đang được sửa đổi và do đó độc quyền bị khóa.Sau khi hoàn thành thao tác - hãy xóa tệp khóa. Phương pháp này giả định rằng lệnh tạo tập tin là nguyên tử.
  2. Sử dụng API giao dịch NTFS (nếu thích hợp).
  3. Tạo liên kết tới tệp, viết tệp đã thay đổi dưới tên ngẫu nhiên (ví dụ: Guid.NewGuid()) - và sau đó remap liên kết tới tệp mới. Tất cả người đọc sẽ truy cập tệp thông qua liên kết (tên được biết).

Dĩ nhiên tất cả 3 phương pháp tiếp cận có nhiều nhược điểm và ưu điểm

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