2009-05-14 37 views
37

tôi dường như không thể tìm thấy một cách hiệu quả hơn để "sao chép" một nguồn tài nguyên nhúng vào đĩa, hơn như sau:Viết tập tin từ dòng nguồn lắp ráp vào đĩa

using (BinaryReader reader = new BinaryReader(
    assembly.GetManifestResourceStream(@"Namespace.Resources.File.ext"))) 
{ 
    using (BinaryWriter writer 
     = new BinaryWriter(new FileStream(path, FileMode.Create))) 
    { 
     long bytesLeft = reader.BaseStream.Length; 
     while (bytesLeft > 0) 
     { 
      // 65535L is < Int32.MaxValue, so no need to test for overflow 
      byte[] chunk = reader.ReadBytes((int)Math.Min(bytesLeft, 65536L)); 
      writer.Write(chunk); 

      bytesLeft -= chunk.Length; 
     } 
    } 
} 

Có vẻ như không trực tiếp hơn cách để làm bản sao, trừ khi tôi đang thiếu một cái gì đó ...

+2

Có vẻ ổn với tôi. Nó có cảm thấy quá nhiều dòng mã không? – Cheeso

+0

Cảm giác như có cách trực tiếp hơn là chunking. – user7116

Trả lời

61

tôi không chắc chắn lý do tại sao bạn đang sử dụng BinaryReader/BinaryWriter tại al l. Cá nhân tôi muốn bắt đầu với một phương pháp tiện ích hữu ích:

public static void CopyStream(Stream input, Stream output) 
{ 
    // Insert null checking here for production 
    byte[] buffer = new byte[8192]; 

    int bytesRead; 
    while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     output.Write(buffer, 0, bytesRead); 
    } 
} 

sau đó gọi nó là:

using (Stream input = assembly.GetManifestResourceStream(resourceName)) 
using (Stream output = File.Create(path)) 
{ 
    CopyStream(input, output); 
} 

Bạn có thể thay đổi kích thước bộ đệm tất nhiên, hoặc có nó như một tham số để phương pháp này - nhưng điểm chính là đây là mã đơn giản hơn. Nó có hiệu quả hơn không? Không. Bạn có chắc chắn thực sự cần cần mã này hiệu quả hơn không? Bạn có thực sự có hàng trăm MB bạn cần phải ghi ra đĩa không?

Tôi thấy tôi hiếm khi cần mã để cực kỳ hiệu quả, nhưng tôi hầu như luôn luôn cần nó để được đơn giản. Sự khác biệt về hiệu suất mà bạn có thể thấy giữa phương pháp này và cách tiếp cận "thông minh" (nếu có sẵn) không phải là hiệu ứng thay đổi phức tạp (ví dụ O (n) thành O (log n)) - và đó là loại hiệu suất đạt được mà thực sự có thể đáng để theo đuổi.

CHỈNH SỬA: Như đã lưu ý trong nhận xét, .NET 4.0 có Stream.CopyTo do đó bạn không cần phải tự mình mã hóa.

+0

Vinh quang, tôi đoán tôi đã đánh rơi con mồi để bỏ qua lớp Stream. Nghèo, người nghèo Stream. – user7116

+0

Tệp được đề cập nằm trong khoảng từ 5-10MB, vì vậy nó không đáng kể về mặt tốc độ.Tôi đã tìm kiếm một cái gì đó đơn giản/súc tích (vì đơn giản/ngắn gọn có xu hướng có nghĩa là hiệu quả). – user7116

+0

Tôi đã thử thay đổi thành CopyTo() và chạy vào rất nhiều "Quá trình không thể truy cập tệp vì nó đang được sử dụng bởi một quá trình khác." lỗi từ ASP.NET (nguyên nhân Stream vẫn mở). Tôi đã quay lại sử dụng các cách sử dụng để làm rõ điều đó. – eduncan911

2

Cá nhân tôi sẽ làm điều đó theo cách này:

using (BinaryReader reader = new BinaryReader(
    assembly.GetManifestResourceStream(@"Namespace.Resources.File.ext"))) 
{ 
    using (BinaryWriter writer 
     = new BinaryWriter(new FileStream(path, FileMode.Create))) 
    { 
     byte[] buffer = new byte[64 * 1024]; 
     int numread = reader.Read(buffer,0,buffer.Length); 

     while (numread > 0) 
     { 
      writer.Write(buffer,0,numread); 
      numread = reader.Read(buffer,0,buffer.Length); 
     } 

     writer.Flush(); 
    } 
} 
+0

Tôi thích rằng trừ đi tuôn ra, nhưng không bao gồm một cách trực tiếp hơn, tôi nghĩ tôi sẽ đưa bạn như là câu trả lời. – user7116

2

Bạn sẽ phải viết một vòng lặp, nếu đó là câu hỏi của bạn. Nhưng bạn có thể làm mà không có người đọc và người viết vì luồng cơ bản đã giao dịch với dữ liệu byte [].

Đây là khoảng như nhỏ gọn như tôi có thể nhận được:

using (Stream inStream = File.OpenRead(inputFile)) 
using (Stream outStream = File.OpenWrite(outputFile)) 
{ 
    int read; 
    byte[] buffer = new byte[64 * 1024]; 

    while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     outStream.Write(buffer, 0, read); 
    } 
} 
49

Nếu tài nguyên (file) là nhị phân.

File.WriteAllBytes("C:\ResourceName", Resources.ResourceName); 

Và nếu tài nguyên (tệp) là văn bản.

File.WriteAllText("C:\ResourceName", Resources.ResourceName); 
+4

Đây là câu trả lời hợp lý duy nhất! Mọi người khác đang cung cấp các cấu trúc vô lý với nhiều luồng, bộ đệm và hơn 10 dòng khi một dòng này thực hiện tương tự. – Traubenfuchs

+0

Cách tiếp cận này xem xét ít nhất là không đầy đủ. Tài liệu .NET 4.0 cho [File.WriteAllBytes] (https://msdn.microsoft.com/en-us/library/system.io.file.writeallbytes%28v=vs.100%29.aspx) chỉ ra rằng tài liệu thứ hai đối số là một mảng byte có nội dung được ghi vào tệp. Và tài liệu cho [File.WriteAllText] (https://msdn.microsoft.com/en-us/library/ms143375%28v=vs.100%29.aspx) cho biết đối số thứ hai là chuỗi để ghi vào tập tin. Vì vậy, chỉ định 'Resources.ResourceName' làm đối số thứ hai không thực sự hợp lý. – DavidRR

+0

Nếu 'Resources.ResourceName' là kiểu nhị phân của' Resources.ResourceName' là 'Byte []' theo yêu cầu, và nó sẽ hoạt động như mong đợi. Điều này làm cho nó hoàn chỉnh/rõ ràng cho bạn? – KoalaBear

14

Tôi thực sự đã kết thúc bằng cách sử dụng dòng đơn này: Assembly.GetExecutingAssembly().GetManifestResourceStream("[Project].[File]").CopyTo(New FileStream(FileLocation, FileMode.Create)). Tất nhiên, điều này là dành cho .Net 4.0

Cập nhật: Tôi thấy rằng dòng ở trên có thể giữ một tệp bị khóa sao cho SQLite báo cáo rằng cơ sở dữ liệu là chỉ đọc. Do đó, tôi đã kết thúc với những điều sau:

Using newFile As Stream = New FileStream(FileLocation, FileMode.Create) 
    Assembly.GetExecutingAssembly().GetManifestResourceStream("[Project].[File]").CopyTo(newFile) 
End Using 
+3

+1, chắc chắn là cách để đi .Net 4.0+. Tôi cũng lưu ý một cách nhanh chóng để tạo 'FileStream' là với các phương thức tĩnh trên đối tượng 'File', như [' File.Create() '] (http://msdn.microsoft.com/en-us /library/system.io.file.create.aspx). – user7116

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