C# WebClient下载TFS2015工作项附件损坏 手动下载同URL文件正常
问题根因及修复方案
存在的错误点
WebClient默认未启用HTTP自动解压缩,TFS2015返回的附件流多数为gzip/deflate压缩格式,直接读取/保存会得到损坏的二进制内容;手动下载时浏览器会自动完成解压缩,因此手动下载的文件可以正常使用。- 代码混用
async/await和.Result阻塞调用,容易触发异步上下文死锁,同时可能出现流未完全读取就进入上传逻辑的情况,最终生成损坏文件。 - 使用
FileMode.OpenOrCreate打开本地文件时,若存在同名旧文件且体积大于新下载文件,新文件尾部会残留旧文件的冗余内容,导致文件损坏。 WebClient请求TFS附件接口时缺少Accept: application/octet-stream请求头,部分版本TFS会返回包含错误信息的HTML页面而非二进制附件流,也会导致文件损坏。
修正后的实现代码
推荐使用已取代WebClient的HttpClient实现,完整代码如下:
public async Task<WorkItem> AddAttachmentFromSourceURL(int workItemID, string url, string fileName) { using var handler = new HttpClientHandler() { // 按需替换为硬编码凭据 UseDefaultCredentials = true, // 开启自动解压缩,处理gzip/deflate压缩的响应 AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }; using var httpClient = new HttpClient(handler); // 指定接受二进制流响应 httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/octet-stream")); // 直接读取流上传,不需要落盘,避免本地文件操作异常 await using var attStream = await httpClient.GetStreamAsync(url); // 全程使用await,禁止用.Result阻塞调用 var attachment = await WorkItemTrackingHttpClient.CreateAttachmentAsync(uploadStream: attStream, fileName: fileName); var jsonPatchDoc = new JsonPatchDocument(); jsonPatchDoc.Add(new JsonPatchOperation() { Operation = Operation.Add, Path = "/relations/-", Value = new { rel = "AttachedFile", url = attachment.Url, attributes = new { comment = "Comments for the file " + fileName } } }); return await WorkItemTrackingHttpClient.UpdateWorkItemAsync(jsonPatchDoc, workItemID); }
额外验证建议
测试阶段可以先将下载的流保存到本地,和手动下载的文件做MD5校验,确认下载环节无问题后再走上传逻辑。
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

