You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 09:52:55