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

C#使用WebClient下载MP4触发WebException:文件被其他进程占用

解决WebClient批量下载MP4时“文件被其他进程占用”的问题

看起来你遇到的这个问题很典型——只有MP4文件触发“被占用”错误,而且是首次下载,大概率是系统级进程抢先锁定了刚下载(或正在下载)的MP4文件,而非你的代码本身重复占用。下面我拆解下原因和对应的解决方案:

核心原因分析

MP4是媒体文件,Windows系统自带的媒体预览服务(比如Windows Explorer的缩略图生成)、Windows Search索引器,或者第三方杀毒软件实时扫描,会在文件开始写入磁盘后立刻尝试读取文件内容,这时候WebClient还没完全释放文件句柄,就会触发“被占用”的IO异常。而Parallel.ForEach的高并行度(20个线程)会放大这个问题——同时下载的MP4越多,系统进程争抢文件句柄的概率就越高。

解决方案

1. 先降低并行度试试(快速验证)

把MaxDegreeOfParallelism从20降到5-10,减少同时下载的MP4数量,给系统进程足够的时间处理单个文件,避免扎堆争抢:

Parallel.ForEach(
    files,
    new ParallelOptions { MaxDegreeOfParallelism = 8 }, // 调低并行数
    currentFile => { DownloadFile(currentFile); }
);

2. 下载到临时文件,完成后再重命名(最可靠的方案)

通过“临时文件中转”的方式,绕开系统进程对目标文件的锁定。因为同一磁盘卷下的文件重命名是原子操作,几乎不会被打断:

private static void DownloadFile(AFile file) {
    string tempPath = null;
    try {
        // 生成唯一临时文件名
        tempPath = Path.Combine(Path.GetDirectoryName(file.SaveLocation), 
                               $"temp_{Guid.NewGuid()}.tmp");
        
        using (var webClient = new WebClient()) {
            webClient.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0)");
            // 先下载到临时文件
            webClient.DownloadFile(new Uri(file.Address), tempPath);
        }

        // 确保目标文件不存在后,重命名临时文件为最终文件名
        if (File.Exists(file.SaveLocation)) {
            File.Delete(file.SaveLocation);
        }
        File.Move(tempPath, file.SaveLocation);
    } catch (WebException e) {
        Logger.Warn(e.ToString());
        FailedDownloadCount++;
    } finally {
        // 无论成功失败,清理临时文件
        if (!string.IsNullOrEmpty(tempPath) && File.Exists(tempPath)) {
            try {
                File.Delete(tempPath);
            } catch { /* 忽略清理失败的情况 */ }
        }
        System.Threading.Thread.Sleep(250);
    }
}

3. 排查并排除系统进程的干扰

如果想精准定位是哪个进程在占用文件,可以用Process Explorer工具:

  • 当报错时,立刻搜索目标MP4文件的路径,查看哪个进程持有该文件的句柄。
  • 如果是Windows预览服务,可以临时关闭Windows Search服务,或者把下载目录添加到索引排除列表;如果是杀毒软件,把下载目录加入实时扫描的白名单。

4. 改用HttpClient替代WebClient(可选优化)

WebClient是较旧的API,HttpClient在资源管理和异步支持上更完善。如果你的项目是.NET 6+,可以用Parallel.ForEachAsync配合HttpClient实现异步并行下载,资源释放更及时:

public static class DownloadUtilities { 
    public static int FailedDownloadCount; 
    // 复用HttpClient实例,避免频繁创建连接
    private static readonly HttpClient _httpClient = new HttpClient();

    static DownloadUtilities() {
        _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/4.0 (compatible; MSIE 8.0)");
    }

    public static async Task DownloadLinks(List<AFile> files) { 
        FailedDownloadCount = 0; 
        await Parallel.ForEachAsync(files, 
            new ParallelOptions { MaxDegreeOfParallelism = 10 }, 
            async (currentFile, ct) => { 
                await DownloadFileAsync(currentFile, ct); 
            } 
        ); 
    } 

    private static async Task DownloadFileAsync(AFile file, CancellationToken ct) { 
        string tempPath = Path.Combine(Path.GetDirectoryName(file.SaveLocation), 
                                      $"temp_{Guid.NewGuid()}.tmp");
        try {
            using (var response = await _httpClient.GetStreamAsync(file.Address, ct)) {
                using (var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true)) {
                    await response.CopyToAsync(fileStream, ct);
                }
            }

            if (File.Exists(file.SaveLocation)) {
                File.Delete(file.SaveLocation);
            }
            File.Move(tempPath, file.SaveLocation);
        } catch (Exception e) {
            Logger.Warn(e.ToString());
            FailedDownloadCount++;
        } finally {
            if (File.Exists(tempPath)) {
                try { File.Delete(tempPath); } catch {}
            }
            await Task.Delay(250, ct);
        }
    } 
}

总结

优先尝试临时文件中转的方案,这是最通用且能彻底解决问题的办法;同时调低并行度可以减少触发问题的概率。如果想找到根因,用Process Explorer排查占用进程是最直接的方式。

内容的提问来源于stack exchange,提问作者user5405648

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:35:29