.NET 6中如何优雅地从文件服务器URI批量复制文件到本地
优雅地从HTTP文件服务器下载目录下所有文件到本地(.NET 6)
情况1:文件服务器支持WebDAV协议
如果你的文件服务器支持WebDAV(比如IIS配置了WebDAV扩展、Apache开启mod_dav等),这是最优雅的解决方案——不需要解析HTML,直接通过WebDAV协议枚举文件并下载。
实现代码
using System.Net.Http; using System.Xml.Linq; public static async Task DownloadFilesViaWebDavAsync(string sourceUrl, string targetDir) { // 确保本地目标目录存在 Directory.CreateDirectory(targetDir); using var httpClient = new HttpClient(); // 发送PROPFIND请求获取当前目录的文件列表 var propfindRequest = new HttpRequestMessage(HttpMethod.Propfind, sourceUrl); propfindRequest.Headers.Add("Depth", "1"); // 仅获取当前层级的内容,不递归子目录 var response = await httpClient.SendAsync(propfindRequest); response.EnsureSuccessStatusCode(); // 确保请求成功 var xmlContent = await response.Content.ReadAsStringAsync(); var xmlDoc = XDocument.Parse(xmlContent); var davNamespace = XNamespace.Get("DAV:"); // 遍历所有文件条目,排除目录项 foreach (var responseElement in xmlDoc.Descendants(davNamespace + "response")) { var fileHref = responseElement.Element(davNamespace + "href")?.Value; if (string.IsNullOrWhiteSpace(fileHref)) continue; // 跳过目录链接(以上斜杠结尾的都是目录) if (fileHref.EndsWith("/")) continue; // 构建完整的文件下载URL var fullFileUrl = new Uri(new Uri(sourceUrl), fileHref).ToString(); var fileName = Path.GetFileName(fileHref); var localFilePath = Path.Combine(targetDir, fileName); // 下载并保存文件 using var localFileStream = new FileStream(localFilePath, FileMode.Create, FileAccess.Write); await httpClient.GetStreamAsync(fullFileUrl).CopyToAsync(localFileStream); } }
情况2:文件服务器仅返回HTML目录列表
如果服务器只能返回默认的HTML索引页面(就像你遇到的情况),用专业的HTML解析库替代手写正则,代码更可靠、易维护。
步骤1:安装AngleSharp NuGet包
先在项目中安装AngleSharp(一款轻量且强大的HTML解析库):
Install-Package AngleSharp
实现代码
using AngleSharp; using AngleSharp.Dom; using System.Net.Http; public static async Task DownloadFilesFromHtmlIndexAsync(string sourceUrl, string targetDir) { Directory.CreateDirectory(targetDir); using var httpClient = new HttpClient(); var htmlPageContent = await httpClient.GetStringAsync(sourceUrl); // 初始化AngleSharp的解析环境 var browsingContext = BrowsingContext.New(Configuration.Default); var htmlDocument = await browsingContext.OpenAsync(req => req.Content(htmlPageContent)); // 提取所有文件链接:排除上级目录(../)和子目录(以/结尾) var fileLinks = htmlDocument.QuerySelectorAll("pre a") .Where(linkElement => !linkElement.GetAttribute("href").StartsWith("../") && !linkElement.GetAttribute("href").EndsWith("/")) .Select(linkElement => linkElement.GetAttribute("href")); foreach (var relativeFilePath in fileLinks) { var fullFileUrl = new Uri(new Uri(sourceUrl), relativeFilePath).ToString(); var fileName = Path.GetFileName(relativeFilePath); var localFilePath = Path.Combine(targetDir, fileName); // 下载文件到本地 using var localFileStream = new FileStream(localFilePath, FileMode.Create, FileAccess.Write); await httpClient.GetStreamAsync(fullFileUrl).CopyToAsync(localFileStream); } }
额外优化建议
- 异常处理:可以添加try-catch块处理单个文件下载失败的情况,避免整个任务中断
- 并行下载:使用
.NET 6+支持的Parallel.ForEachAsync实现多文件并行下载,提升效率(注意控制并发数,避免压垮服务器) - 覆盖控制:可以判断本地文件是否存在,按需跳过或覆盖
- 进度跟踪:如果需要显示下载进度,可使用
HttpCompletionOption.ResponseHeadersRead配合流读取来实现实时进度更新
内容的提问来源于stack exchange,提问作者CrazyEight
相关产品推荐
相关产品推荐

