C# net8 实现下载文件至本地
API代码
[HttpPost]
[Route("DownloadFile")]
[ApiDescriptionSettings(Tag = "下载文件")]
public async Task<IActionResult> DownloadFile(string id, bool isGroup)
{
try
{
await _fileService.DownloadFile(id, isGroup);
// 获取文件路径列表
List<FileUploadAttachment> filePaths = await _fileService.GetFilePathAsync(id, isGroup);
// 检查传入的文件路径列表是否为空
if (filePaths == null || filePaths.Count == 0)
{
return new BadRequestObjectResult("文件路径列表不能为空");
}
// 单个文件下载处理
if (filePaths.Count == 1)
{
string filePath = filePaths[0].Path;
// 检查文件是否存在
if (!System.IO.File.Exists(filePath))
{
return new NotFoundObjectResult($"文件不存在: {filePath}");
}
// 读取文件内容
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
string extName = StaticData.GetFileExt(filePaths[0].ExtName);//文件后缀转换
// 返回文件下载响应
return new FileContentResult(fileBytes, extName)
{
FileDownloadName = filePaths[0].FileName
};
}
// 多个文件下载处理 - 创建ZIP压缩包
using (var memoryStream = new MemoryStream())
{
using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (FileUploadAttachment fi in filePaths)
{
string filePath = fi.Path;
// 跳过不存在的文件
if (!System.IO.File.Exists(filePath))
{
continue;
}
try
{
// 在ZIP文件中创建条目
string entryName = Path.GetFileName(filePath);
ZipArchiveEntry entry = zipArchive.CreateEntry(entryName);
// 将文件内容写入ZIP条目
using (Stream entryStream = entry.Open())
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
await fileStream.CopyToAsync(entryStream);
}
}
catch (Exception ex)
{
// 记录错误但继续处理其他文件
Console.WriteLine($"处理文件 {filePath} 时出错: {ex.Message}");
}
}
// 检查ZIP中是否有文件
if (zipArchive.Entries.Count == 0)
{
return new NotFoundObjectResult("所有请求的文件都不存在");
}
}
// 准备ZIP文件下载
memoryStream.Position = 0;
string zipFileName = $"{id}_{DateTime.Now:yyyyMMddHHmmss}.zip";
// 返回ZIP文件下载响应
return new FileContentResult(memoryStream.ToArray(), "application/zip")
{
FileDownloadName = zipFileName
};
}
}
catch (Exception ex)
{
// 返回服务器内部错误信息
return new ObjectResult($"服务器内部错误: {ex.Message}")
{
StatusCode = (int)HttpStatusCode.InternalServerError
};
}
}
前端实现 VUE3 + ElementPlus
const downloadFile = (file) => {
const formData = new FormData();
formData.append("id", "D8AF64DE0F514A1FB093746D1F5AEBA5");
formData.append("isGroup", false);
hfApi
.DownloadFile(formData)
.then((res) => {
console.log(res);
console.log('Blob验证111:', new Blob([res.request.response], {
type: res.headers["content-type"] || "application/octet-stream",
}));
console.log('Blob验证:', res.request.response instanceof Blob); // 应该输出true
for (const key in res) {
console.log(`Blob验证${key}:`, res[key] instanceof Blob);
}
//console.log(res.headers);
//console.log(res.data);
// 修改点1:获取二进制数据
let fileName = file.name;
const contentDisposition = res.headers["content-disposition"];
if (contentDisposition) {
// 改进后的正则表达式,处理带编码文件名和不同格式
const fileNameMatch = contentDisposition.match(
/filename\*?=([^']+)'\w*'([^;]+)|filename="?([^";]+)"?/i
);
if (fileNameMatch) {
// 优先使用UTF-8编码的文件名(filename*=格式)
fileName = fileNameMatch[2]
? decodeURIComponent(fileNameMatch[2])
: fileNameMatch[3] || file.name;
}
}
// 直接从响应数据创建Blob
const blob = new Blob([res.request.response], {
type: res.headers["content-type"] || "application/octet-stream",
});
// 创建下载链接
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = fileName;
link.style.display = "none";
document.body.appendChild(link);
link.click();
// 清理
setTimeout(() => {
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}, 100);
})
.catch((error) => {
ElMessage.error(`文件下载失败:${error}`);
});
};
问题:
能下载文件,但是下载的文件大小不一致并且打开不了文件
接口返回的是一个图片,能直接预览,我物理文件实际是35kb,下载后变成了50kb
这是接口请求配置
接口响应以及控制台打印,请求响应就36.7kb
下载后就变成五十多kb了
index.vue
{
"data": "�PNG\r\n*****0IEND�B�", "status": 200, "statusText": "OK", "headers": { "content-disposition": "attachment; filename=2025-03-12_10-31-55.png; filename*=UTF-8''2025-03-12_10-31-55.png", "content-length": "36302", "content-type": "image/png" }, "config": { "transitional": { "silentJSONParsing": true, "forcedJSONParsing": true, "clarifyTimeoutError": false }, "transformRequest": [ null ], "transformResponse": [ null ], "timeout": 5000, "xsrfCookieName": "XSRF-TOKEN", "xsrfHeaderName": "X-XSRF-TOKEN", "maxContentLength": -1, "maxBodyLength": -1, "env": { "FormData": null }, "headers": { "Accept": "application/json, text/plain, */*", "Authorization": "Bearer eyJhbGciOiJIUzI1iLCJhdWQiOiJwb3dlcmJ5IEZ1cmlvbiJ9.ZEIS8b9XQiI_RZI60eCclynw04pMggMYcmnupZg7EyU", "apisign": "T!B$FMU=#_F(-_VBF((GN=F*P*YFZ@=LFAV-KFKOOT=F+MXQSHE()XPF%VQ-F^PY*HECQ^&==" }, "method": "post", "url": "http://localhost:6538/api/file/DownloadFile", "responseType": "blob", "data": {} }, "request": { "custom": { "events": {}, "requestHeaders": {}, "responseHeaders": {}, "method": "POST", "url": "http://localhost:6538/api/file/DownloadFile", "async": true, "options": { "url": "http://localhost:6538/api/file/DownloadFile", "type": "POST", "body": {} }, "timeout": 20, "xhr": {} }, "readyState": 4, "responseURL": "http://localhost:6538/api/file/DownloadFile", "status": 200, "statusText": "OK", "responseType": "", "response": "�PNG\r\n\*****0IEND�B
�",
"responseXML": null,
"timeout": 5000
}
}
这是我请求返回的参数
求大佬解惑,为什么就下载的不正确
先确定问题,然后才能解决问题。
1、api:
单独测试api,按照你描述的图片能预览到,那说明api没问题。
2、vue:
检查前端vue的调用问题。请求时候responseType:'blob'参数就ok了。
看你截图是responseType不对的问题。service封装问题?直接用axios是ok的。
无responseType: "blob",
responseType: "blob",
api也是有blob的
已经用了其他方式解决,不通过blob了,最终的问题是编码问题,二进制被多次转码,怎么转都会有误差
解决方案就是直接传编码,前端通过编码转换为文件