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

与罗伯特哈维的回答相同,但使用 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

既然您提到加载需要很长时间,那么我将使用异步文件读取来加快加载过程。硬盘是计算机中最慢的组件。谷歌在谷歌浏览器上进行异步读写以缩短加载时间。在以前的工作中,我不得不在 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 回答