440

大きなバイナリファイル(数メガバイト)をバイト配列に読み込むWebサーバーがあります。サーバーは同時に複数のファイルを読み取っている可能性があるため(異なるページ要求)、CPUに過度の負担をかけずにこれを行うための最も最適化された方法を探しています。以下のコードで十分ですか?

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

12 に答える 12

848

全体を次のように置き換えるだけです。

return File.ReadAllBytes(fileName);

ただし、メモリ消費が心配な場合は、ファイル全体を一度にメモリに読み込むことはできません。あなたはそれをチャンクで行うべきです。

于 2010-01-08T21:27:08.003 に答える
78

ここでの答えは一般的に「しない」であると私は主張するかもしれません。一度にすべてのデータが絶対に必要な場合を除いて、ベースのAPI(またはリーダー/イテレーターのバリアント)の使用を検討してくださいStream。これは、システムの負荷を最小限に抑え、スループットを最大化するために、(質問で示唆されているように)複数の並列操作がある場合に特に重要です。

たとえば、発信者にデータをストリーミングしている場合:

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);
    }
}
于 2010-01-08T21:44:33.273 に答える
36

私はこれを考えるでしょう:

byte[] file = System.IO.File.ReadAllBytes(fileName);
于 2010-01-08T21:28:44.270 に答える
34

あなたのコードはこれに因数分解することができます(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;
} 

Integer.MaxValue-Readメソッドによって設定されたファイルサイズの制限に注意してください。つまり、一度に読み取ることができるのは2GBのチャンクのみです。

また、FileStreamの最後の引数はバッファサイズであることに注意してください。

FileStreamBufferedStreamについても読むことをお勧めします。

いつものように、最も速いプロファイルを作成するための単純なサンプルプログラムが最も有益です。

また、基盤となるハードウェアはパフォーマンスに大きな影響を及ぼします。大容量キャッシュを備えたサーバーベースのハードディスクドライブと、オンボードメモリキャッシュを備えたRAIDカードを使用していますか?または、IDEポートに接続された標準ドライブを使用していますか?

于 2010-01-08T21:36:57.903 に答える
10

操作の頻度、ファイルのサイズ、および表示しているファイルの数に応じて、考慮すべき他のパフォーマンスの問題があります。覚えておくべきことの1つは、各バイト配列がガベージコレクターに翻弄されて解放されるということです。そのデータをキャッシュしていないと、大量のガベージが作成され、パフォーマンスの大部分がGCの%Timeまで失われる可能性があります。。チャンクが85Kより大きい場合は、ラージオブジェクトヒープ(LOH)に割り当てることになります。これには、すべての世代のコレクションを解放する必要があります(これは非常にコストがかかり、サーバー上では、実行中にすべての実行が停止します) )。さらに、LOHに大量のオブジェクトがある場合、LOHの断片化(LOHが圧縮されることはありません)が発生し、パフォーマンスが低下し、メモリ不足の例外が発生する可能性があります。特定のポイントに達したらプロセスをリサイクルできますが、それがベストプラクティスかどうかはわかりません。

重要なのは、アプリのライフサイクル全体を検討してからでないと、すべてのバイトを可能な限り最速でメモリに読み込む必要があります。そうしないと、短期的なパフォーマンスと全体的なパフォーマンスを交換する可能性があります。

于 2010-01-08T22:25:19.947 に答える
8

BinaryReaderは大丈夫だと思いますが、バッファの長さを取得するためのコードのすべての行の代わりに、これにリファクタリングすることができます:

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

を使用するよりも優れているはずです。これは、コメント投稿者の1人が600 MBを超えるファイルで問題が発生した.ReadAllBytes()ことを含むトップレスポンスのコメントで見たためです。これは、この種のことを目的としているためです。また、ステートメントに入れると、とが閉じられて破棄されます。.ReadAllBytes()BinaryReaderusingFileStreamBinaryReader

于 2016-10-12T00:18:24.630 に答える
2

「大きなファイル」が4GBの制限を超えることを意味する場合は、次の記述されたコードロジックが適切です。注意すべき重要な問題は、SEEKメソッドで使用されるLONGデータ型です。LONGは、2^32のデータ境界を超えてポイントすることができます。この例では、コードは最初に1GBのチャンクで大きなファイルを処理し、1GBの大きなチャンク全体が処理された後、残りの(<1GB)バイトが処理されます。このコードを使用して、4GBサイズを超えるファイルのCRCを計算します。(この例のcrc32cの計算にはhttps://crc32c.machinezoo.com/を使用します)

private uint Crc32CAlgorithmBigCrc(string fileName)
{
    uint hash = 0;
    byte[] buffer = null;
    FileInfo fileInfo = new FileInfo(fileName);
    long fileLength = fileInfo.Length;
    int blockSize = 1024000000;
    decimal div = fileLength / blockSize;
    int blocks = (int)Math.Floor(div);
    int restBytes = (int)(fileLength - (blocks * blockSize));
    long offsetFile = 0;
    uint interHash = 0;
    Crc32CAlgorithm Crc32CAlgorithm = new Crc32CAlgorithm();
    bool firstBlock = true;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[blockSize];
        using (BinaryReader br = new BinaryReader(fs))
        {
            while (blocks > 0)
            {
                blocks -= 1;
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(blockSize);
                if (firstBlock)
                {
                    firstBlock = false;
                    interHash = Crc32CAlgorithm.Compute(buffer);
                    hash = interHash;
                }
                else
                {
                    hash = Crc32CAlgorithm.Append(interHash, buffer);
                }
                offsetFile += blockSize;
            }
            if (restBytes > 0)
            {
                Array.Resize(ref buffer, restBytes);
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(restBytes);
                hash = Crc32CAlgorithm.Append(interHash, buffer);
            }
            buffer = null;
        }
    }
    //MessageBox.Show(hash.ToString());
    //MessageBox.Show(hash.ToString("X"));
    return hash;
}
于 2019-04-26T04:16:45.373 に答える
2

概要:画像がaction =埋め込みリソースとして追加されている場合は、GetExecutingAssemblyを使用してjpgリソースをストリームに取得し、ストリーム内のバイナリデータをバイト配列に読み込みます。

   public byte[] GetAImage()
    {
        byte[] bytes=null;
        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "MYWebApi.Images.X_my_image.jpg";

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        {
            bytes = new byte[stream.Length];
            stream.Read(bytes, 0, (int)stream.Length);
        }
        return bytes;

    }
于 2020-06-15T21:45:07.483 に答える
0

パフォーマンスを向上させるには、C#でBufferedStreamクラスを使用します。バッファは、データをキャッシュするために使用されるメモリ内のバイトのブロックであり、それによってオペレーティングシステムへの呼び出しの数を減らします。バッファは読み取りと書き込みのパフォーマンスを向上させます。

コード例と追加の説明については、以下を参照してください。http: //msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

于 2010-01-08T21:37:45.593 に答える
0

これを使って:

 bytesRead = responseStream.ReadAsync(buffer, 0, Length).Result;
于 2019-04-13T07:39:37.497 に答える
-4

大きなファイルを提供するために、このResponse.TransferFile()方法を試してみることをお勧めします。Response.Flush()Response.End()

于 2010-01-19T23:37:55.937 に答える
-7

2 GBを超えるファイルを処理している場合は、上記の方法が失敗することがわかります。

ストリームをMD5に渡して、ファイルをチャンク化できるようにするだけの方がはるかに簡単です。

private byte[] computeFileHash(string filename)
{
    MD5 md5 = MD5.Create();
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        byte[] hash = md5.ComputeHash(fs);
        return hash;
    }
}
于 2014-10-20T09:56:23.613 に答える