2013-07-23 35 views
8

Tôi có một ứng dụng ghi các tệp lớn trong nhiều phân đoạn. Tôi sử dụng FileStream.Seek để định vị từng wirte. Có vẻ như khi tôi gọi FileStream.Write ở vị trí sâu trong một tệp thưa thớt, ghi sẽ kích hoạt thao tác "chèn lấp" (ghi 0) trên tất cả các byte trước đó chậm.Cách tạo luồng phim nhanh và hiệu quả ghi trên các tệp thưa thớt lớn

Có cách nào hiệu quả hơn trong việc xử lý tình huống này không?

Mã bên dưới minh họa sự cố. Viết ban đầu mất khoảng 370 MS trên máy tính của tôi.

public void WriteToStream() 
    { 
     DateTime dt; 
     using (FileStream fs = File.Create("C:\\testfile.file")) 
     { 
      fs.SetLength(1024 * 1024 * 100); 
      fs.Seek(-1, SeekOrigin.End); 
      dt = DateTime.Now; 
      fs.WriteByte(255);    
     } 

     Console.WriteLine(@"WRITE MS: " + DateTime.Now.Subtract(dt).TotalMilliseconds.ToString()); 
    } 

Trả lời

7

NTFS hỗ trợ Sparse Files, tuy nhiên không có cách nào để thực hiện trong .net mà không cần p/gọi một số phương pháp gốc. Nó không phải là rất khó để đánh dấu một tập tin thưa thớt, chỉ biết một khi một tập tin được đánh dấu là một tập tin thưa thớt nó không bao giờ có thể được chuyển đổi trở lại vào một tập tin không thưa thớt ngoại trừ bằng cách đối phó toàn bộ tập tin vào một không mới tệp thưa thớt.

Ví dụ năng bảo mật bằng

class Program 
{ 
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    private static extern bool DeviceIoControl(
     SafeFileHandle hDevice, 
     int dwIoControlCode, 
     IntPtr InBuffer, 
     int nInBufferSize, 
     IntPtr OutBuffer, 
     int nOutBufferSize, 
     ref int pBytesReturned, 
     [In] ref NativeOverlapped lpOverlapped 
    ); 

    static void MarkAsSparseFile(SafeFileHandle fileHandle) 
    { 
     int bytesReturned = 0; 
     NativeOverlapped lpOverlapped = new NativeOverlapped(); 
     bool result = 
      DeviceIoControl(
       fileHandle, 
       590020, //FSCTL_SET_SPARSE, 
       IntPtr.Zero, 
       0, 
       IntPtr.Zero, 
       0, 
       ref bytesReturned, 
       ref lpOverlapped); 
     if(result == false) 
      throw new Win32Exception(); 
    } 

    static void Main() 
    { 
     //Use stopwatch when benchmarking, not DateTime 
     Stopwatch stopwatch = new Stopwatch(); 

     stopwatch.Start(); 
     using (FileStream fs = File.Create(@"e:\Test\test.dat")) 
     { 
      MarkAsSparseFile(fs.SafeFileHandle); 

      fs.SetLength(1024 * 1024 * 100); 
      fs.Seek(-1, SeekOrigin.End); 
      fs.WriteByte(255); 
     } 
     stopwatch.Stop(); 

     //Returns 2 for sparse files and 1127 for non sparse 
     Console.WriteLine(@"WRITE MS: " + stopwatch.ElapsedMilliseconds); 
    } 
} 

Khi tệp đã được đánh dấu là thưa thớt nó bây giờ cư xử như bạn trừ nó để hành xử trong các ý kiến ​​quá. Bạn không cần phải viết một byte để đánh dấu một tệp với kích thước đã đặt.

static void Main() 
{ 
    string filename = @"e:\Test\test.dat"; 

    using (FileStream fs = new FileStream(filename, FileMode.Create)) 
    { 
     MarkAsSparseFile(fs.SafeFileHandle); 

     fs.SetLength(1024 * 1024 * 25); 
    } 
} 

enter image description here

+0

Cảm ơn bạn đã phản ứng, điều này rất thú vị. Tôi đã có ấn tượng rằng FileStream.SetLength() đã tạo ra một tệp thưa thớt. Tôi có sai về điều đó không? Tôi đang cố gắng tránh tác động đến hiệu suất phát sinh khi ghi vào một tệp thưa thớt tại một điểm tìm kiếm lớn. Tôi không hoàn toàn rõ ràng về cách này sẽ tránh được vấn đề đó. – revoxover

+0

Nội bộ SetLength đang gọi [SetEndOfFile] (http://msdn.microsoft.com/en-us/library/aa365531%28v=vs.85%29.aspx). Tôi không biết nếu đó là tạo ra một tập tin thưa thớt hay không. –

+1

Chỉ cần chạy một thử nghiệm nhanh chóng, nó không. –

1

Dưới đây là một số mã để sử dụng tập tin thưa thớt:

using System; 
using System.ComponentModel; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Threading; 

using Microsoft.Win32.SafeHandles; 

public static class SparseFiles 
{ 
    private const int FILE_SUPPORTS_SPARSE_FILES = 64; 

    private const int FSCTL_SET_SPARSE = 0x000900c4; 

    private const int FSCTL_SET_ZERO_DATA = 0x000980c8; 

    public static void MakeSparse(this FileStream fileStream) 
    { 
     var bytesReturned = 0; 
     var lpOverlapped = new NativeOverlapped(); 
     var result = DeviceIoControl(
      fileStream.SafeFileHandle, 
      FSCTL_SET_SPARSE, 
      IntPtr.Zero, 
      0, 
      IntPtr.Zero, 
      0, 
      ref bytesReturned, 
      ref lpOverlapped); 

     if (!result) 
     { 
      throw new Win32Exception(); 
     } 
    } 

    public static void SetSparseRange(this FileStream fileStream, long fileOffset, long length) 
    { 
     var fzd = new FILE_ZERO_DATA_INFORMATION(); 
     fzd.FileOffset = fileOffset; 
     fzd.BeyondFinalZero = fileOffset + length; 
     var lpOverlapped = new NativeOverlapped(); 
     var dwTemp = 0; 

     var result = DeviceIoControl(
      fileStream.SafeFileHandle, 
      FSCTL_SET_ZERO_DATA, 
      ref fzd, 
      Marshal.SizeOf(typeof(FILE_ZERO_DATA_INFORMATION)), 
      IntPtr.Zero, 
      0, 
      ref dwTemp, 
      ref lpOverlapped); 
     if (!result) 
     { 
      throw new Win32Exception(); 
     } 
    } 

    public static bool SupportedOnVolume(string filename) 
    { 
     var targetVolume = Path.GetPathRoot(filename); 
     var fileSystemName = new StringBuilder(300); 
     var volumeName = new StringBuilder(300); 
     uint lpFileSystemFlags; 
     uint lpVolumeSerialNumber; 
     uint lpMaxComponentLength; 

     var result = GetVolumeInformationW(
      targetVolume, 
      volumeName, 
      (uint)volumeName.Capacity, 
      out lpVolumeSerialNumber, 
      out lpMaxComponentLength, 
      out lpFileSystemFlags, 
      fileSystemName, 
      (uint)fileSystemName.Capacity); 
     if (!result) 
     { 
      throw new Win32Exception(); 
     } 

     return (lpFileSystemFlags & FILE_SUPPORTS_SPARSE_FILES) == FILE_SUPPORTS_SPARSE_FILES; 
    } 

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool DeviceIoControl(
     SafeFileHandle hDevice, 
     int dwIoControlCode, 
     IntPtr InBuffer, 
     int nInBufferSize, 
     IntPtr OutBuffer, 
     int nOutBufferSize, 
     ref int pBytesReturned, 
     [In] ref NativeOverlapped lpOverlapped); 

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool DeviceIoControl(
     SafeFileHandle hDevice, 
     int dwIoControlCode, 
     ref FILE_ZERO_DATA_INFORMATION InBuffer, 
     int nInBufferSize, 
     IntPtr OutBuffer, 
     int nOutBufferSize, 
     ref int pBytesReturned, 
     [In] ref NativeOverlapped lpOverlapped); 

    [DllImport("kernel32.dll", EntryPoint = "GetVolumeInformationW")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool GetVolumeInformationW(
     [In] [MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, 
     [Out] [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpVolumeNameBuffer, 
     uint nVolumeNameSize, 
     out uint lpVolumeSerialNumber, 
     out uint lpMaximumComponentLength, 
     out uint lpFileSystemFlags, 
     [Out] [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpFileSystemNameBuffer, 
     uint nFileSystemNameSize); 

    [StructLayout(LayoutKind.Sequential)] 
    private struct FILE_ZERO_DATA_INFORMATION 
    { 
     public long FileOffset; 

     public long BeyondFinalZero; 
    } 
} 

Và mẫu mã để kiểm tra các lớp trên.

class Program 
{ 
    static void Main(string[] args) 
    { 
     using (var fileStream = new FileStream("test", FileMode.Create, FileAccess.ReadWrite, FileShare.None)) 
     { 
      fileStream.SetLength(1024 * 1024 * 128); 
      fileStream.MakeSparse(); 
      fileStream.SetSparseRange(0, fileStream.Length); 
     } 
    } 
} 

Hope this helps

+0

LƯU Ý: Windows Explorer không biết cách sao chép các tệp thưa thớt. Nó sẽ sao chép tất cả dữ liệu 0 byte dưới dạng dữ liệu byte 0 thực tế. Vì vậy, nếu bạn muốn duy trì sự thưa thớt, bạn có lẽ nên giữ một số siêu dữ liệu trong tệp của mình về các vùng thưa thớt để bạn có thể khôi phục chúng sau khi quản trị viên (người dùng) sao chép tệp. – Paul