如何确保PDF文件完全下载后再执行重命名操作?
嘿,这个问题我太有共鸣了!用DownloadFileAsync这种异步下载方法时,靠Thread.Sleep来等文件下完真的是碰运气——网络好的时候浪费时间,网络差的时候直接搞坏文件。咱们换个靠谱的方式来解决:
核心思路:跟踪异步下载任务的完成状态
异步方法的优势是不阻塞线程,但你需要明确知道所有任务什么时候结束,而不是瞎猜等待时间。这里有两种最实用的方案:
方案1:用Async/Await + Task.WhenAll(推荐,简洁高效)
从.NET 4.5开始,WebClient提供了DownloadFileTaskAsync方法,它返回Task对象,可以用await来等待单个任务完成,或者用Task.WhenAll批量等待所有下载任务结束。
修改后的代码如下(注意要把Main改成异步方法,C# 7.1及以上版本支持):
using System; using System.Net; using HtmlAgilityPack; using System.IO; using System.Threading.Tasks; using System.Collections.Generic; namespace Crawler { class Program { static async Task Main(string[] args) { var downloadTasks = new List<Task>(); HtmlDocument htmlDoc = new HtmlWeb().Load("http://example.com"); HtmlNodeCollection ProductListPage = htmlDoc.DocumentNode.SelectNodes("//div[@class='productContain padb6']//div[@class='large-4 medium-4 columns']/a"); foreach (HtmlNode src in ProductListPage) { htmlDoc = new HtmlWeb().Load(src.Attributes["href"].Value); HtmlNodeCollection LinkTester = htmlDoc.DocumentNode.SelectNodes("//div[@class='row padt6 padb4']//a"); if (LinkTester != null) { foreach (var dllink in LinkTester) { string LinkURL = dllink.Attributes["href"].Value; Console.WriteLine($"开始下载:{LinkURL}"); string ExtractFilename = LinkURL.Substring(LinkURL.LastIndexOf("/")); string savePath = Path.Combine(@"C:\temp", ExtractFilename); var DLClient = new WebClient(); // 将下载任务加入列表,不立即等待 downloadTasks.Add(DLClient.DownloadFileTaskAsync(new Uri(LinkURL), savePath)); } } } Console.WriteLine("等待所有文件下载完成..."); // 等待所有异步下载任务全部结束 await Task.WhenAll(downloadTasks); Console.WriteLine("所有文件下载完成!"); // 现在放心执行重命名 var files = Directory.GetFiles(@"C:\temp\", "*.pdf"); foreach (var file in files) { // 注意:只替换文件名里的-,不要碰路径部分 string fileName = Path.GetFileName(file); string newFileName = Path.Combine(Path.GetDirectoryName(file), fileName.Replace("-", " ")); File.Move(file, newFileName); Console.WriteLine($"重命名完成:{file} -> {newFileName}"); } } } }
几个关键改进点:
- 用
DownloadFileTaskAsync替代DownloadFileAsync,让异步任务可跟踪 - 把所有下载任务收集到
List<Task>中,最后用Task.WhenAll一次性等待全部完成 - 修复了原代码中
file.Replace("-", " ")的问题:原代码会替换整个路径中的-,现在只替换文件名部分,避免路径出错 - 增加了控制台日志,方便查看下载和重命名进度
方案2:用同步下载(简单但低效)
如果你的项目不支持异步Main,或者不需要并行下载,可以直接用WebClient.DownloadFile同步方法——它会阻塞当前线程,直到单个文件下载完成再继续下一个。这种方式不需要处理异步任务,代码更简单,但下载是串行的,速度会慢一些:
// 替换原循环里的异步下载代码 foreach (var dllink in LinkTester) { string LinkURL = dllink.Attributes["href"].Value; Console.WriteLine($"开始下载:{LinkURL}"); string ExtractFilename = LinkURL.Substring(LinkURL.LastIndexOf("/")); string savePath = Path.Combine(@"C:\temp", ExtractFilename); var DLClient = new WebClient(); // 同步下载,直到完成才会进入下一次循环 DLClient.DownloadFile(new Uri(LinkURL), savePath); } // 循环结束后,所有文件肯定已经下载完成,直接执行重命名即可
额外提示:异常处理
如果担心下载过程中出现网络错误,可以在下载任务里加try-catch,比如:
downloadTasks.Add(Task.Run(async () => { try { await DLClient.DownloadFileTaskAsync(new Uri(LinkURL), savePath); Console.WriteLine($"下载成功:{LinkURL}"); } catch (Exception ex) { Console.WriteLine($"下载失败:{LinkURL},错误:{ex.Message}"); } }));
这样即使某个文件下载失败,也不会影响其他任务和后续的重命名操作。
内容的提问来源于stack exchange,提问作者10101
相关产品推荐
相关产品推荐

