a

Câu hỏi Cách tốt nhất để đọc một tập tin lớn thành một mảng byte trong C #?


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?

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;
}

307
2018-01-08 21:24


gốc


Ví dụ của bạn có thể được viết tắt thành byte[] buff = File.ReadAllBytes(fileName). - Jesse C. Slicer
Tại sao nó là một webservice của bên thứ ba ngụ ý các tập tin cần phải được đầy đủ trong RAM trước khi được gửi đến webservice, chứ không phải là streamed? Webservice sẽ không biết sự khác biệt. - Brian
@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
@sjeffrey: Tôi đã nói rằng dữ liệu sẽ được truyền trực tuyến, không được truyền dưới dạng luồng .NET. Các khách hàng sẽ không biết sự khác biệt theo cách nào. - Brian


Các câu trả lời:


Chỉ cần thay thế toàn bộ nội dung bằng:

return File.ReadAllBytes(fileName);

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


634
2018-01-08 21:27



phương pháp này được giới hạn trong 2 ^ 32 byte tệp (4.2 GB) - Mahmoud Farahat
File.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) - juanjo.arana
@ juanjo.arana Yeah, ừm ... tất nhiên sẽ luôn có thứ gì đó 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: msdn.microsoft.com/en-us/library/hh285054%28v=vs.110%29.aspx - Mehrdad Afshari
Có một giới hạn cho kích thước mảng trong .NET, nhưng trong .NET 4.5 bạn có thể bật hỗ trợ cho mảng lớn (> 2GB) bằng cách sử dụng tùy chọn cấu hình đặc biệt xem msdn.microsoft.com/en-us/library/hh285054.aspx - illegal-immigrant
@harag Không, và đó không phải là câu hỏi đặt ra. - Mehrdad Afshari


Tôi có thể lập luận rằng câu trả lời ở đây nói chung là 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 cân nhắc sử dụng StreamAPI dựa trên (hoặc một số biến thể của trình đọc / trình lặp). Đó là đặc biệt quan trọng khi bạn có nhiều hoạt động song song (như đề xuất của 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 truyền dữ liệu đến 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);
    }
}

55
2018-01-08 21:44



Để thêm vào câu lệnh của bạn, tôi thậm chí còn đề nghị xem xét các trình xử lý async ASP.NET nếu bạn có một hoạt động liên kết I / O như truyền trực tuyến một tệp đến máy khách. Tuy nhiên, nếu bạn phải đọc toàn bộ tập tin 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. - Mehrdad Afshari
@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. - Marc Gravell♦
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. - Tony_Henrich
API được hệ thống cung cấp là gì? - Tony_Henrich
@ Tony: Tôi đã nói trong câu trả lời của tôi: File.ReadAllBytes. - Mehrdad Afshari


Tôi sẽ nghĩ điều này:

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

30
2018-01-08 21:28



Lưu ý rằng điều này có thể ngăn chặn khi nhận được các tệp thực sự lớn. - vapcguy


Mã của bạn có thể được nhân 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 - giới hạn kích thước tệp được đặt bởi phương thức Đọ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 về Tập hồ sơ và BufferedStream.

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.

Ngoài ra 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?


21
2018-01-08 21:36



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? - Tony_Henrich
@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.
Mã này có chứa một lỗi nghiêm trọng. Đọc chỉ được yêu cầu trả lại ít nhất 1 byte. - mafu
Tôi sẽ chắc chắn để bọc dài để int đúc với các kiểm tra xây dựng như thế này: checked ((int) fs.Length) - tzup
Tôi sẽ làm var binaryReader = new BinaryReader(fs); fileData = binaryReader.ReadBytes((int)fs.Length); trong đó using tuyên bố. Nhưng điều đó có hiệu quả giống như những gì OP đã làm, chỉ cần tôi cắt ra một dòng mã bằng cách truyền fs.Length đến int thay vì nhận được long giá trị của FileInfo chiều dài và chuyển đổi điều đó. - vapcguy


Tùy thuộc vào tần suất hoạt động, kích thước của tệp và số lượng tệp bạn đang xem, có các vấn đề về hiệu suất khác 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ời gian trong 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 xem xét vòng đời đầy đủ của ứng dụng của bạn trước khi nhất thiết chỉ đọ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 hiệu suất ngắn hạn cho hiệu suất tổng thể.


8
2018-01-08 22:25



mã nguồn C # về nó, để quản lý garbage collector, chunks, hiệu suất, bộ đếm sự kiện, ... - PreguntonCojoneroCabrón


tôi sẽ nói BinaryReader là tốt, nhưng có thể được refactored này, thay vì tất cả những dòng mã để có đượ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 tốt hơn sử dụng .ReadAllBytes(), vì tôi đã thấy trong các nhận xét về phản hồi hàng đầu bao gồm .ReadAllBytes() rằng một trong những người nhận xét có vấn đề với tệp> 600 MB, vì BinaryReader có nghĩa là cho loại điều này. Ngoài ra, đặt nó trong một using tuyên bố đảm bảo FileStream và BinaryReader được đóng và xử lý.


4
2017-10-12 00:18



Đối với C #, cần sử dụng "using (FileStream fs = File.OpenRead (tên tệp))" thay vì "bằng cách sử dụng (FileStream fs = new File.OpenRead (tên tệp))" như đã cho ở trên. Chỉ cần xóa từ khóa mới trước File.OpenRead () - Syed Mohamed
@Syed Mã trên được viết cho C #, nhưng bạn nói đúng new không cần thiết ở đó. Đã xóa. - vapcguy


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 phần sau để biết ví dụ về mã và giải thích thêm: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx


0
2018-01-08 21:37



Đ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? - Mehrdad Afshari
Anh ấy yêu cầu hiệu suất tốt nhất để không đọc tập tin cùng một lúc. - Todd Moses
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. - Mehrdad Afshari


Tôi sẽ khuyên bạn nên thử Response.TransferFile() phương pháp rồi Response.Flush() và Response.End() để phục vụ các tệp lớn của bạn.


-3
2018-01-19 23:37