2

ユーザーが大きなファイルをアップロードできるようにしようとしています。ファイルをアップロードする前に、ファイルをまとめたいと思います。各チャンクはC#オブジェクトである必要があります。その理由は、ロギングを目的としています。長い話ですが、各ファイルチャンクを表す実際のC#オブジェクトを作成する必要があります。とにかく、私は次のアプローチを試みています:

public static List<FileChunk> GetAllForFile(byte[] fileBytes)
{
  List<FileChunk> chunks = new List<FileChunk>();
  if (fileBytes.Length > 0)
  {
    FileChunk chunk = new FileChunk();
    for (int i = 0; i < (fileBytes.Length / 512); i++)
    {
      chunk.Number = (i + 1);
      chunk.Offset = (i * 512);
      chunk.Bytes = fileBytes.Skip(chunk.Offset).Take(512).ToArray();

      chunks.Add(chunk);
      chunk = new FileChunk();
    }
  }
  return chunks;
}

残念ながら、このアプローチは非常に遅いようです。チャンクごとにオブジェクトを作成しながら、パフォーマンスを向上させる方法を知っている人はいますか?

ありがとうございました

4

4 に答える 4

3

私はこれが少し傷つくだろうと思う:

chunk.Bytes = fileBytes.Skip(chunk.Offset).Take(512).ToArray();

代わりにこれを試してください:

byte buffer = new byte[512];
Buffer.BlockCopy(fileBytes, chunk.Offset, buffer, 0, 512);
chunk.Bytes = buffer;

(コードはテストされていません)

そして、このコードが遅くなる可能性が高い理由は、Skipが配列に対して特別なことを何もしないためです(可能ですが)。これは、ループを通過するたびに、配列の最初の512 * nアイテムが繰り返されることを意味します。これにより、O(n ^ 2)のパフォーマンスが得られ、O(n)が表示されるはずです。

于 2012-08-09T17:51:58.263 に答える
2

次のようなものを試してください(テストされていないコード):

public static List<FileChunk> GetAllForFile(string fileName, FileMode.Open)
{
  var chunks = new List<FileChunk>();
  using (FileStream stream = new FileStream(fileName))
  {
      int i = 0;
      while (stream.Position <= stream.Length)
      {
          var chunk = new FileChunk();
          chunk.Number = (i);
          chunk.Offset = (i * 512);
          Stream.Read(chunk.Bytes, 0, 512);
          chunks.Add(chunk);
          i++;
      }
  }
  return chunks;
}

上記のコードは、プロセスのいくつかのステップをスキップし、ファイルからバイトを直接読み取ることを優先します。

ファイルが512の偶数倍でない場合、最後のチャンクには512バイト未満が含まれることに注意してください。

于 2012-08-09T17:52:08.740 に答える
1

Robert Harveyの答えと同じですが、BinaryReaderを使用しているので、オフセットを指定する必要はありません。もう一方の端でBinaryWriterを使用してファイルを再アセンブルする場合、FileChunkのOffsetメンバーは必要ありません。

public static List<FileChunk> GetAllForFile(string fileName) {
    var chunks = new List<FileChunk>();
    using (FileStream stream = new FileStream(fileName)) {
        BinaryReader reader = new BinaryReader(stream);
        int i = 0;
        bool eof = false;
        while (!eof) {
            var chunk = new FileChunk();
            chunk.Number = i;
            chunk.Offset = (i * 512);
            chunk.Bytes = reader.ReadBytes(512);
            chunks.Add(chunk);
            i++;
            if (chunk.Bytes.Length < 512) { eof = true; }
        }
    }
    return chunks;
}

パケット損失とデータ破損を補うために何をするかについて考えましたか?

于 2012-08-09T19:12:01.870 に答える
1

読み込みに時間がかかるとおっしゃっていたので、読み込みプロセスを高速化するために非同期ファイル読み取りを使用します。ハードディスクは、コンピュータの最も遅いコンポーネントです。Google は、読み込み時間を改善するために、Google Chrome で非同期の読み取りと書き込みを行います。以前の仕事では、C# でこのようなことをしなければなりませんでした。

アイデアは、ファイルのさまざまな部分に対していくつかの非同期リクエストを生成することです。次に、リクエストが届いたら、バイト配列を取得し、一度に 512 バイトの FileChunk オブジェクトを作成します。これにはいくつかの利点があります。

  1. これを別のスレッドで実行すると、プログラム全体が大きなファイルのロードを待機することはありません。
  2. バイト配列を処理して FileChunk オブジェクトを作成できますが、ハードディスクはまだファイルの他の部分で読み取り要求を埋めようとしています。
  3. 保持できる保留中の読み取り要求の量を制限すると、RAM スペースを節約できます。これにより、ハード ディスクへのページ フォールトが少なくなり、RAM と CPU キャッシュをより効率的に使用できるようになり、処理がさらに高速化されます。

FileStream クラスで次のメソッドを使用する必要があります。

[HostProtectionAttribute(SecurityAction.LinkDemand, ExternalThreading = true)]
public virtual IAsyncResult BeginRead(
    byte[] buffer,
    int offset,
    int count,
    AsyncCallback callback,
    Object state
)

public virtual int EndRead(
    IAsyncResult asyncResult
)

また、これは asyncResult で得られるものです:

// Extract the FileStream (state) out of the IAsyncResult object
FileStream fs = (FileStream) ar.AsyncState;

// Get the result
Int32 bytesRead = fs.EndRead(ar);

読んでいただきたい参考資料をご紹介します。

これは、非同期ファイル I/O モデルを操作するコード サンプルです。

これは、非同期ファイル I/Oに関する MS ドキュメントのリファレンスです。

于 2012-08-09T19:31:33.840 に答える