2010-01-08 82 views
286

Tôi có một máy chủ web sẽ đọc các tệp nhị phân lớn (vài megabyte) vào các mảng byte. Các máy chủ có thể được đọc một số tập tin cùng một lúc (yêu cầu trang khác nhau), vì vậy tôi đang tìm cách tối ưu nhất để làm điều này mà không cần đánh thuế CPU quá nhiều. Mã dưới đây có đủ tốt không?Cách tốt nhất để đọc một tệp lớn thành một mảng byte trong C#?

public byte[] FileToByteArray(string fileName) 
{ 
    byte[] buff = null; 
    FileStream fs = new FileStream(fileName, 
            FileMode.Open, 
            FileAccess.Read); 
    BinaryReader br = new BinaryReader(fs); 
    long numBytes = new FileInfo(fileName).Length; 
    buff = br.ReadBytes((int) numBytes); 
    return buff; 
} 
+50

Ví dụ của bạn có thể được viết tắt thành 'byte [] buff = File.ReadAllBytes (tên tệp)'. –

+3

Tại sao dịch vụ web của bên thứ ba ngụ ý rằng tệp cần phải đầy đủ trong RAM trước khi được gửi tới webservice, thay vì được phát trực tuyến? Webservice sẽ không biết sự khác biệt. – Brian

+0

@Brian, Một số khách hàng không biết cách xử lý một luồng .NET như Java chẳng hạn. Khi đây là trường hợp tất cả những gì có thể được thực hiện là đọc toàn bộ tập tin trong mảng byte. – sjeffrey

Trả lời

599

Chỉ cần thay thế toàn bộ điều với:

return File.ReadAllBytes(fileName); 

Tuy nhiên, nếu bạn lo lắng về việc tiêu thụ bộ nhớ, bạn nên không đọc toàn bộ tập tin vào bộ nhớ cùng một lúc ở tất cả. Bạn nên làm điều đó theo từng phần.

+24

phương pháp này được giới hạn ở 2^32 byte tệp (4.2 GB) –

+8

Tệp.ReadAllBytes ném OutOfMemoryException với các tệp lớn (được thử nghiệm với tệp 630 MB và không thành công) –

+3

@ juanjo.arana Yeah, well ... tất nhiên sẽ có luôn là thứ không phù hợp với trí nhớ, trong trường hợp đó, không có câu trả lời cho câu hỏi. Nói chung, bạn nên truyền tệp và không lưu trữ nó trong bộ nhớ hoàn toàn. Bạn có thể muốn xem xét điều này cho một biện pháp tạm dừng: http://msdn.microsoft.com/en-us/library/hh285054%28v=vs.110%29.aspx –

29

tôi sẽ nghĩ rằng đây:

byte[] file = System.IO.File.ReadAllBytes(fileName); 
20

Mã của bạn có thể là yếu tố này (thay cho File.ReadAllBytes):

public byte[] ReadAllBytes(string fileName) 
{ 
    byte[] buffer = null; 
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) 
    { 
     buffer = new byte[fs.Length]; 
     fs.Read(buffer, 0, (int)fs.Length); 
    } 
    return buffer; 
} 

Lưu ý Integer.MaxValue - kích thước giới hạn tập tin đặt bởi phương pháp đọc. Nói cách khác, bạn chỉ có thể đọc một đoạn 2GB cùng một lúc.

Cũng lưu ý rằng đối số cuối cùng cho FileStream là kích thước bộ đệm.

Tôi cũng khuyên bạn nên đọc khoảng FileStreamBufferedStream.

Như mọi khi, một chương trình mẫu đơn giản cho tiểu sử nhanh nhất sẽ có lợi nhất.

Phần cứng cơ bản của bạn sẽ có ảnh hưởng lớn đến hiệu suất. Bạn có đang sử dụng ổ đĩa cứng dựa trên máy chủ có bộ nhớ cache lớn và thẻ RAID có bộ nhớ cache trên bo mạch không? Hay bạn đang sử dụng ổ đĩa tiêu chuẩn được kết nối với cổng IDE?

+0

Tại sao loại phần cứng tạo nên sự khác biệt? Vì vậy, nếu đó là IDE bạn sử dụng một số phương pháp NET và nếu nó RAID bạn sử dụng khác? –

+0

@Tony_Henrich - Nó không có gì để làm với những gì các cuộc gọi bạn thực hiện từ ngôn ngữ lập trình của bạn. Có nhiều loại ổ đĩa cứng khác nhau. Ví dụ, ổ đĩa Seagate được phân loại là "AS" hoặc "NS" với NS là máy chủ dựa trên, ổ đĩa lưu trữ lớn, nơi-như là "AS" ổ đĩa là người tiêu dùng - nhà máy tính dựa trên ổ đĩa. Tốc độ tìm kiếm và tốc độ truyền tải nội bộ cũng ảnh hưởng đến tốc độ bạn có thể đọc một cái gì đó từ đĩa. Các mảng RAID có thể cải thiện hiệu suất đọc/ghi thông qua bộ nhớ đệm. Vì vậy, bạn có thể đọc tất cả các tập tin cùng một lúc, nhưng phần cứng cơ bản vẫn là yếu tố quyết định. –

+2

Mã này chứa lỗi nghiêm trọng. Đọc chỉ được yêu cầu trả lại ít nhất 1 byte. – mafu

0

Sử dụng lớp BufferedStream trong C# để cải thiện hiệu suất. Bộ đệm là một khối byte trong bộ nhớ được sử dụng để lưu trữ dữ liệu, do đó giảm số lượng cuộc gọi đến hệ điều hành. Bộ đệm cải thiện hiệu năng đọc và ghi.

Xem sau đây cho một ví dụ mã và giải thích thêm: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

+0

Điểm của việc sử dụng 'BufferedStream' khi bạn đọc toàn bộ nội dung cùng một lúc là gì? –

+0

Ông yêu cầu hiệu suất tốt nhất để không đọc tập tin cùng một lúc. –

+8

Hiệu suất có thể đo lường được trong ngữ cảnh hoạt động. Bộ đệm bổ sung cho luồng mà bạn đang đọc tuần tự, tất cả cùng một lúc, vào bộ nhớ không có khả năng hưởng lợi từ bộ đệm bổ sung. –

52

tôi có thể tranh luận rằng câu trả lời ở đây thường là "không". Trừ khi bạn hoàn toàn cần tất cả dữ liệu cùng một lúc, hãy xem xét sử dụng API Stream-based (hoặc một số biến thể của trình đọc/trình lặp). Đó là đặc biệt là quan trọng khi bạn có nhiều thao tác song song (như được đề xuất bởi câu hỏi) để giảm thiểu tải hệ thống và tối đa hóa thông lượng.

Ví dụ, nếu bạn đang trực tuyến dữ liệu đến một người gọi:

Stream dest = ... 
using(Stream source = File.OpenRead(path)) { 
    byte[] buffer = new byte[2048]; 
    int bytesRead; 
    while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) { 
     dest.Write(buffer, 0, bytesRead); 
    } 
} 
+2

Để thêm vào tuyên bố của bạn, tôi thậm chí còn đề nghị xem xét xử lý async ASP.NET nếu bạn có một I/O ràng buộc hoạt động như streaming một tập tin cho khách hàng. Tuy nhiên, nếu bạn * phải * đọc toàn bộ tệp thành một 'byte []' vì một lý do nào đó, tôi khuyên bạn nên tránh sử dụng luồng hoặc bất kỳ thứ gì khác và chỉ sử dụng API do hệ thống cung cấp. –

+0

@Mehrdad - đã đồng ý; nhưng bối cảnh đầy đủ không rõ ràng. Tương tự như vậy MVC có kết quả hành động cho việc này. –

+0

Có, tôi cần tất cả dữ liệu cùng một lúc. Nó sẽ đến một webservice của bên thứ ba. –

7

Tùy thuộc vào tần số hoạt động, kích thước của các tập tin, và số lượng các tập tin mà bạn đang xem, có khác các vấn đề hiệu suất cần xem xét. Một điều cần nhớ, là mỗi mảng byte của bạn sẽ được phát hành tại lòng thương xót của bộ thu gom rác. Nếu bạn không lưu trữ bất kỳ dữ liệu nào trong số đó, bạn có thể sẽ tạo ra nhiều rác và mất hầu hết hiệu suất của mình thành % Time in GC.Nếu các khối lớn hơn 85K, bạn sẽ được cấp phát cho Heap đối tượng lớn (LOH), nó sẽ yêu cầu một bộ sưu tập của tất cả các thế hệ để giải phóng (điều này là rất tốn kém, và trên một máy chủ sẽ ngừng tất cả thực hiện trong khi nó đang xảy ra). Ngoài ra, nếu bạn có một tấn đối tượng trên LOH, bạn có thể kết thúc với phân mảnh LOH (LOH không bao giờ được nén) dẫn đến hiệu suất kém và không có ngoại lệ bộ nhớ. Bạn có thể tái chế quy trình khi bạn đạt đến một điểm nhất định, nhưng tôi không biết đó có phải là phương pháp hay nhất hay không.

Vấn đề là, bạn nên cân nhắc toàn bộ vòng đời của ứng dụng trước khi đọc tất cả các byte vào bộ nhớ một cách nhanh nhất có thể hoặc bạn có thể giao dịch ngắn hạn cho hiệu suất tổng thể.

-3

Tôi khuyên bạn nên thử phương pháp Response.TransferFile() sau đó là Response.Flush()Response.End() để phân phát các tệp lớn của bạn.

-6

Nếu bạn đang xử lý các tệp ở trên 2   GB, bạn sẽ thấy rằng các phương thức trên không thành công.

Đó là dễ dàng hơn nhiều chỉ để trao suối tắt để MD5 và cho phép điều đó để đoạn tập tin của bạn dành cho bạn:

private byte[] computeFileHash(string filename) 
{ 
    MD5 md5 = MD5.Create(); 
    using (FileStream fs = new FileStream(filename, FileMode.Open)) 
    { 
     byte[] hash = md5.ComputeHash(fs); 
     return hash; 
    } 
} 
+9

Tôi không thấy cách mã có liên quan đến câu hỏi (hoặc những gì bạn đề xuất trong văn bản) –

2

Tôi muốn nói BinaryReader là tốt, nhưng có thể được refactored vào đó, thay vì tất cả những dòng mã để nhận được chiều dài của bộ đệm:

public byte[] FileToByteArray(string fileName) 
{ 
    byte[] fileData = null; 

    using (FileStream fs = File.OpenRead(fileName)) 
    { 
     using (BinaryReader binaryReader = new BinaryReader(fs)) 
     { 
      fileData = binaryReader.ReadBytes((int)fs.Length); 
     } 
    } 
    return fileData; 
} 

nên được tốt hơn so với sử dụng .ReadAllBytes(), kể từ khi tôi nhìn thấy trong các ý kiến ​​về phản ứng đầu bao gồm .ReadAllBytes() rằng một trong những bình luận có proble ms với các tập tin> 600 MB, vì một BinaryReader có nghĩa là cho loại điều này. Ngoài ra, đặt nó trong tuyên bố using đảm bảo rằng FileStreamBinaryReader bị đóng và xử lý.

+0

Đối với C#, cần sử dụng "sử dụng (FileStream fs = File.OpenRead (tên tệp))" thay vì "sử dụng (FileStream fs = new File.OpenRead (tên tệp))" như đã nêu ở trên. Chỉ cần loại bỏ từ khóa mới trước khi File.OpenRead() – Syed

+0

@Syed Mã trên được viết cho C#, nhưng bạn nói đúng là 'mới' không cần thiết ở đó. Đã xóa. – vapcguy

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