如何在C#中编程下载大文件

10

我需要以编程方式下载一个大文件并在处理它之前进行一些操作。有什么最好的方法可以做到这一点?由于文件很大,我想要指定等待时间,以便我可以强制退出。

我知道可以使用WebClient.DownloadFile()来下载文件。但是似乎没有办法指定等待时间以便强制退出。

try
{
    WebClient client = new WebClient();
    Uri uri = new Uri(inputFileUrl);
    client.DownloadFile(uri, outputFile);
}
catch (Exception ex)
{
    throw;
}

另一种方法是使用命令行工具(wget)下载文件,然后使用ProcessStartInfo来执行命令,并使用Process的WaitForExit(int ms)强制退出。

ProcessStartInfo startInfo = new ProcessStartInfo();
//set startInfo object

try
{
    using (Process exeProcess = Process.Start(startInfo))
    {
        //wait for time specified
        exeProcess.WaitForExit(1000 * 60 * 60);//wait till 1m

        //check if process has exited
        if (!exeProcess.HasExited)
        {
            //kill process and throw ex
            exeProcess.Kill();
            throw new ApplicationException("Downloading timed out");
        }
    }
}
catch (Exception ex)
{
    throw;
}

有更好的方法吗?请帮帮忙,谢谢。


我尝试了第一个,它运行良好。 - Stan Huang at Taiwan
4个回答

25

使用WebRequest获取响应流,然后从响应流中读取字节块,并将每个块写入目标文件。这样,您可以控制何时停止下载,如果下载时间过长,则在块之间获得控制,并且可以基于时钟决定是否已超时:

        DateTime startTime = DateTime.UtcNow;
        WebRequest request = WebRequest.Create("http://www.example.com/largefile");
        WebResponse response = request.GetResponse();
        using (Stream responseStream = response.GetResponseStream()) {
            using (Stream fileStream = File.OpenWrite(@"c:\temp\largefile")) { 
                byte[] buffer = new byte[4096];
                int bytesRead = responseStream.Read(buffer, 0, 4096);
                while (bytesRead > 0) {       
                    fileStream.Write(buffer, 0, bytesRead);
                    DateTime nowTime = DateTime.UtcNow;
                    if ((nowTime - startTime).TotalMinutes > 5) {
                        throw new ApplicationException(
                            "Download timed out");
                    }
                    bytesRead = responseStream.Read(buffer, 0, 4096);
                }
            }
        }

@orip,这有什么复杂的吗? - juan
@Juan,首先它是同步的。这个示例的异步版本会有很大不同。但也抛弃了非常用户友好的WebClient外观,它隐藏了在90%的时间里基本上无关紧要的流管理细节。 - Josh
1
Orip,你的代码简单多了。使用Remus的代码的一个优点是我可以知道文件的哪一部分已经下载完成。 - hIpPy
@hlpPy:如果你更喜欢使用WebClient.DownloadFileAsync/CancelAsync,你可以使用WebClient.DownloadProgressChanged事件来了解进度。 - Remus Rusanu

8

使用WebClient类中的DownloadFileAsync如何呢?这种方式的好处是,如果操作时间太长,可以通过调用CancelAsync来取消操作。基本上,调用此方法,如果经过指定的一段时间后,就调用Cancel方法。


如果文件的一部分已经被下载了,CancelAsync 方法会保留该部分还是删除它? - aks

3

在这里提问:C#:带有超时的URL下载

最简单的解决方案:

public string GetRequest(Uri uri, int timeoutMilliseconds)
{
    var request = System.Net.WebRequest.Create(uri);
    request.Timeout = timeoutMilliseconds;
    using (var response = request.GetResponse())
    using (var stream = response.GetResponseStream())
    using (var reader = new System.IO.StreamReader(stream))
    {
        return reader.ReadToEnd();
    }
}

更好(更灵活)的解决方案是这个答案,以WebClientWithTimeout辅助类的形式呈现。

4
webrequest.timeout 只计算接收到 HTTP 响应头的时间,而不是直到响应体下载完成的总时间。也就是说,它影响的是 GetResponse 返回的时间。 - Remus Rusanu

2
你可以像@BFree说的那样使用DownloadFileAsync,然后尝试使用以下WebClient事件:
protected virtual void OnDownloadProgressChanged(DownloadProgressChangedEventArgs e);
protected virtual void OnDownloadFileCompleted(AsyncCompletedEventArgs e);

那么你就可以知道进度百分比了。
e.ProgressPercentage

希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接