如何用JavaScript/C#无界面下载多格式文件?解决批量下载仅最后一个生效问题
你当前的代码只下载最后一个文件,问题出在两个地方:一是window.location.assign会直接替换当前页面的导航上下文,前一个下载请求还没触发就被后一个覆盖了;二是setTimeout的用法完全错了——它第一个参数得是函数,你直接写setTimeout(20000)根本不会延迟执行,代码会立刻跳转到第二个URL。下面分别给你JavaScript和C#的无界面批量下载方案:
JavaScript 无界面批量下载方案
这里的“无界面”指的是不需要用户手动点击,自动触发下载,基于浏览器环境实现。
方法1:动态创建<a>标签模拟点击
这是最直接的方案,利用浏览器原生下载行为,适合同源或后端允许跨域的场景。
// 把所有需要下载的文件链接整理成数组 const fileUrls = [ "https://site.site.net/servlet/DocumentServlet?documentId=123456789&action=viewUncontrolledCopy", "https://site.site.net/servlet/DocumentServlet?documentId=987654321&action=viewUncontrolledCopy" ]; // 批量下载函数,可自定义间隔避免浏览器拦截 function batchDownload(urls, delay = 1000) { urls.forEach((url, index) => { setTimeout(() => { const downloadLink = document.createElement('a'); downloadLink.href = url; // 若后端返回了Content-Disposition响应头,可留空让浏览器自动获取文件名 downloadLink.download = `document_${index + 1}`; // 模拟点击触发下载 downloadLink.click(); // 清理临时创建的标签 document.body.removeChild(downloadLink); }, index * delay); }); } // 调用批量下载,间隔2秒避免被浏览器拦截 batchDownload(fileUrls, 2000);
注意事项:
- 跨域场景下,需要后端配置CORS,且响应头要包含
Access-Control-Expose-Headers: Content-Disposition才能获取服务器返回的文件名。 - 间隔时间别太短,否则浏览器可能会判定为恶意批量下载并拦截。
方法2:Fetch API 获取Blob后下载
这种方案更灵活,能处理跨域(需后端允许),还能在下载前做校验(比如检查文件大小、类型)。
const fileUrls = [ "https://site.site.net/servlet/DocumentServlet?documentId=123456789&action=viewUncontrolledCopy", "https://site.site.net/servlet/DocumentServlet?documentId=987654321&action=viewUncontrolledCopy" ]; // 单个文件下载函数 async function downloadSingleFile(url, defaultFilename) { try { const response = await fetch(url); if (!response.ok) throw new Error(`请求失败,状态码:${response.status}`); // 从响应头提取服务器返回的文件名 const contentDisposition = response.headers.get('Content-Disposition'); let finalFilename = defaultFilename; if (contentDisposition) { const filenameMatch = contentDisposition.match(/filename="?([^"]+)"?/); if (filenameMatch) finalFilename = filenameMatch[1]; } // 转换为Blob并创建下载链接 const fileBlob = await response.blob(); const blobUrl = URL.createObjectURL(fileBlob); const downloadLink = document.createElement('a'); downloadLink.href = blobUrl; downloadLink.download = finalFilename; downloadLink.click(); // 释放Blob URL资源 URL.revokeObjectURL(blobUrl); document.body.removeChild(downloadLink); } catch (err) { console.error(`下载失败:${err.message}`); } } // 批量下载函数,串行执行避免拦截 async function batchDownloadWithFetch(urls, delay = 1500) { for (let i = 0; i < urls.length; i++) { await downloadSingleFile(urls[i], `temp_file_${i + 1}`); // 等待指定间隔后再下载下一个 await new Promise(resolve => setTimeout(resolve, delay)); } } // 启动批量下载 batchDownloadWithFetch(fileUrls);
C# 无界面批量下载方案
如果需要完全脱离浏览器的无界面下载(比如后台自动化任务、控制台程序),C#的HttpClient是最佳选择。
控制台程序示例
using System; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Threading.Tasks; class BatchFileDownloader { static async Task Main(string[] args) { // 整理需要下载的文件链接 var fileUrls = new List<string> { "https://site.site.net/servlet/DocumentServlet?documentId=123456789&action=viewUncontrolledCopy", "https://site.site.net/servlet/DocumentServlet?documentId=987654321&action=viewUncontrolledCopy" }; // 设置文件保存目录,自动创建不存在的目录 string saveDirectory = Path.Combine(Environment.CurrentDirectory, "DownloadedDocuments"); Directory.CreateDirectory(saveDirectory); // 复用HttpClient实例,避免资源泄漏 using var httpClient = new HttpClient(); // 根据文件大小调整超时时间 httpClient.Timeout = TimeSpan.FromMinutes(10); foreach (var url in fileUrls) { try { Console.WriteLine($"开始下载:{url}"); // 获取响应流(先读取头信息,再处理内容) using var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); // 校验请求是否成功 // 确定文件名:优先用服务器返回的文件名,否则用documentId+扩展名 string fileName = "unknown_document"; if (response.Content.Headers.ContentDisposition != null) { fileName = response.Content.Headers.ContentDisposition.FileName.Trim('"'); } else { // 从URL中提取documentId var documentId = url.Split("documentId=")[1].Split('&')[0]; fileName = $"document_{documentId}"; // 根据Content-Type推断文件扩展名 var contentType = response.Content.Headers.ContentType.MediaType; var fileExtension = contentType switch { "application/pdf" => ".pdf", "application/msword" => ".doc", "application/vnd.ms-excel" => ".xls", _ => "" }; fileName += fileExtension; } string savePath = Path.Combine(saveDirectory, fileName); // 将响应流写入本地文件 using var fileStream = new FileStream(savePath, FileMode.Create); await response.Content.CopyToAsync(fileStream); Console.WriteLine($"下载完成:{savePath}"); // 可选:添加间隔,避免给服务器造成过大压力 await Task.Delay(2000); } catch (Exception ex) { Console.WriteLine($"下载失败 {url}:{ex.Message}"); } } Console.WriteLine("所有下载任务已完成"); } }
注意事项:
- 务必复用HttpClient实例,频繁创建会导致Socket资源泄漏。
- 如果目标网站需要登录,需在HttpClient中添加Cookie或Authorization请求头(比如Bearer Token)。
- 可根据实际需求调整超时时间、重试逻辑等。
内容的提问来源于stack exchange,提问作者Rubens Mendes
相关产品推荐
相关产品推荐

