2

我使用分段 http 下载,使用范围 (HttpWebRequest.AddRanges()) 将文件分成几部分,然后加入所有部分。如果一个文件是 100 字节,我将它分成 4 个部分(0-24、25-49、50-74、75-99)并调用四次 DownloadPart 函数。每个部分都使用异步编程模型(BeginRead/EndRead)下载。当所有部分都完成后,我加入他们,part1+ part2+ ...

 public void DowloadPart(HttpWebResponse httpResp)
    {
        Stream httpStm = httpResp.GetResponseStream(); 
        Stream fileStm = new FileStream("myFile.part1", FileMode.Append, FileAccess.Write);
        byte[] buff = new byte[1024 * 16];

        AsyncCallback callback = null;
        callback = ar =>
        {
            int bytesRead = httpStm.EndRead(ar);
            fileStm.Write(buff, 0, bytesRead);
            if(bytesRead == 0)
                return;

            httpStm.BeginRead(buff, 0, buff.Length, callback, null);
        };
        httpStm.BeginRead(buff, 0, buff.Length, callback, null);
    }

我想用直接写入文件来替换加入的部分。由于所有“DownloadPart”都在不同的线程中运行,从不同线程的不同位置同时写入 FileStream 的最佳方法是什么?

4

1 回答 1

4

理想情况下,您首先将文件增长到所需的大小,这并非易事。要使用的 API 仅作为本机 API 存在,即SetFileValidData需要对进程进行特殊SE_MANAGE_VOLUME_PRIVILEGE处理。(巨大的)优势是文件增长到所需的大小而无需先填充 0s。对于那些熟悉数据库的人来说,这称为即时文件初始化

如果你不想麻烦,那么你可以用传统的方式来增长文件,但你会付出两次写入内容的代价,一次是 0,一次是有效内容。

一旦文件大小合适,编写内容的最佳 API 就是WriteFileGather. Scatter/Gather IO 允许您在任意偏移处写入任意文件段。但是,同样,它只是本地的,没有托管的对应物......

W/o scatter/gather IO 另一个不错的选择是内存映射访问。幸运的是,它有一个托管 API,MemoryMappedFile......对于 .Net 4.0。

没有内存映射 IO,您必须使用一个流进行所有写入。这意味着您必须使用生产者-消费者模式,其中 Web 流生成要写入的缓冲区(用要写入的文件偏移量进行注释!),并且一个消费者获取这些缓冲区并将它们写入输出流中的适当位置。

于 2013-10-17T13:03:48.987 回答