我正在开发一个应用程序,该应用程序应将文件从网址下载到计算机,然后对其进行处理。
我完成了任务,一切正常,然后客户转向我说:
我的网速很慢,经常断线,不能直接给链接,否则程序会重新下载文件。
实际上我正在尝试解决这个问题,或者更确切地说,我需要做以下两件事:
- 连接断开后恢复文件下载。- 正如他们所说,在缓冲的帮助下部分解决了短期休息似乎已经消失了。
- 下载现有的“存根”,该存根在上一个不成功的下载过程之后仍然存在。
现在下载文件的过程如下所示:
private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream)
{
var totalBytesRead = 0L;
var readCount = 0L;
var buffer = new byte[4096];
var isMoreToRead = true;
using (var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
{
do
{
var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
isMoreToRead = false;
TriggerProgressChanged(totalDownloadSize, totalBytesRead);
continue;
}
await fileStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
readCount += 1;
if (readCount % 100 == 0)
TriggerProgressChanged(totalDownloadSize, totalBytesRead);
}
while (isMoreToRead);
}
}
也就是说,我们从服务器中取出一块(缓冲区)并将其写入,FileStream
直到服务器为我们提供整个文件。
我试过了:
- 在启动时,将
Stream
's 的位置移动到fileStream.Length
(当前文件大小),但HttpClient
给出通常的Stream
,CanSeek = false
即,我无法从某个点开始从服务器接收数据,只能从一开始。 我试图跳过写入文件(
await fileStream.WriteAsync(buffer, 0, bytesRead);
),直到从服务器接收到的数据数量等于当前文件大小,也就是说,我做了这样的事情(草图):if (totalBytesRead >= fileStream.Length) await fileStream.WriteAsync(buffer, 0, bytesRead);
但是感觉就像是因为我,
await contentStream.ReadAsync(buffer, 0, buffer.Length);
我仍然从服务器下载数据,但我只是不把它写下来,结果我得到了一个损坏的文件,在下载停止的地方正好有一个间隙。在这里,很可能发生这种情况是因为当应用程序关闭和重新加载时缓冲区没有完全写入,我需要更早地开始写入文件 1“缓冲区”,但我不明白如何执行此操作。
简而言之,在应用程序中断/关闭后,我陷入了僵局,不知道如何恢复磁盘上的现有文件。如果你教我从服务器的某个位置获取字节而不影响开头(Seek
in Stream
),那就太好了,那么你可以在休息后请求这些字节并继续下载,或者还有其他选择吗?
总的来说,是的,感谢所有帮助并提出Range Header的人。
我创建了一个方法,该方法通过“回滚”返回当前文件大小到缓冲区大小:
它将返回 0 或
размер файла - размер буфера
.然后我将请求重写为以下内容:
RangeHeaderValue(_fileSize, null);
- 将先前接收到的文件的大小发送到服务器并取走全部余额。如果 0 - 服务器提供一切。_httpClient.SendAsync(...)
- 向服务器发送带有Range
标头的 GET 请求。我
FileStream
设置FileMode.OpenOrCreate
它以使文件不被覆盖。在从服务器读取数据之前,我将位置设置为
FileStream
我之前收到的文件的大小:实际上,继续下载文件所需的一切。代码本身现在是这样的(草稿):
按类型欺凌:
代码成功通过,文件总是被下载并且总是有正确的 Md5 和大小。
它仍然需要清理,把所有东西都整理好,添加一些检查,事件,然后你就可以使用它了)