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

ASP.NET MVC下载文件时fileContents转字符串、丢失文件名问题

问题根因
  • 用return Json()包裹FileContentResult返回时,ASP.NET MVC默认序列化逻辑会自动把byte[]类型的fileContents转成Base64字符串,你看到的无意义字符串就是Base64编码结果,不是乱码,只是没有做对应解码就直接传入Blob构造函数,导致生成的文件损坏。
  • 直接返回FileResult时,$.ajax默认以纯文本格式解析响应体,不会主动读取响应头,而FileDownloadName属性的值最终是被框架写入Content-Disposition响应头的,自然拿不到正确文件名。
方案1:直接返回FileResult + 前端Blob解析(推荐,无额外编解码开销)

这个方案性能最好,适合绝大多数文件下载场景,没有多余的序列化损耗。

后端调整

去掉外层的return Json()包装,直接返回构造好的FileContentResult即可,原有文件初始化逻辑不需要改动:

var fileToDownload = new FileContentResult(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet)
{
    // 文件名含中文/特殊字符时先做URL编码,避免前端读取乱码
    FileDownloadName = Uri.EscapeDataString(fileNameWithExtension)
};
return fileToDownload;

前端调整

给ajax请求添加响应类型配置,强制把响应当成Blob处理,不要让jQuery默认按文本解析,同时从响应头中提取文件名:

$.ajax({
    type: 'POST',
    url: 'mydownloadurl',
    data: uploadFileFormData,
    cache: false,
    contentType: false,
    processData: false,
    // 关键配置:指定响应类型为blob
    xhrFields: {
        responseType: 'blob'
    },
    success: function (data, status, xhr) {
        // 从Content-Disposition头解析文件名
        const disposition = xhr.getResponseHeader('Content-Disposition');
        let fileName = '未命名文件';
        if (disposition && disposition.includes('attachment')) {
            const filenameMatch = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
            if (filenameMatch?.[1]) {
                fileName = filenameMatch[1].replace(/['"]/g, '');
                // 对应后端的URL编码,这里做解码
                fileName = decodeURIComponent(fileName);
            }
        }

        // 此时拿到的data本身就是合法Blob对象,不需要重复构造
        const blob = data;
        const isIE = !!document.documentMode;
        if (isIE) {
            window.navigator.msSaveBlob(blob, fileName);
        } else {
            const objectUrl = (window.URL || window.webkitURL).createObjectURL(blob);
            const a = document.createElement('a');
            a.download = fileName;
            a.href = objectUrl;
            document.body.append(a);
            a.click();
            document.body.removeChild(a);
            // 释放内存,避免内存泄漏
            URL.revokeObjectURL(objectUrl);
        }
    },
    error: function (xhr) {
        console.log('下载失败', xhr.status);
    }
})

如果用axios实现,逻辑完全一致,只需要在请求配置里加responseType: 'blob',再从响应头res.headers['content-disposition']中解析文件名即可。

方案2:JSON结构显式返回文件内容和文件名(适合需要同时返回业务参数的场景)

如果你的接口除了文件还要返回业务状态码、提示信息等内容,必须用JSON格式返回的话,不要直接序列化FileContentResult对象,手动构造返回结构即可。

后端调整

手动将字节数组转为Base64字符串,和文件名、业务状态一起打包返回:

return Json(new {
    success = true,
    msg = "文件生成成功",
    fileBase64 = Convert.ToBase64String(fileBytes),
    fileName = fileNameWithExtension
}, JsonRequestBehavior.AllowGet);

前端调整

拿到JSON响应后,将Base64字符串解码为二进制数组,再构造Blob触发下载:

$.ajax({
    type: 'POST',
    url: 'mydownloadurl',
    data: uploadFileFormData,
    cache: false,
    contentType: false,
    processData: false,
    dataType: 'json',
    success: function (res) {
        if (!res.success) {
            alert(res.msg || '下载失败');
            return;
        }
        // Base64转二进制字节数组
        const binaryStr = atob(res.fileBase64);
        const byteArr = new Uint8Array(binaryStr.length);
        for (let i = 0; i < binaryStr.length; i++) {
            byteArr[i] = binaryStr.charCodeAt(i);
        }
        const blob = new Blob([byteArr], { type: 'application/octet-stream' });
        const fileName = res.fileName;

        // 下载逻辑和方案1一致
        const isIE = !!document.documentMode;
        if (isIE) {
            window.navigator.msSaveBlob(blob, fileName);
        } else {
            const objectUrl = (window.URL || window.webkitURL).createObjectURL(blob);
            const a = document.createElement('a');
            a.download = fileName;
            a.href = objectUrl;
            document.body.append(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(objectUrl);
        }
    },
    error: function (xhr) {
        console.log('请求失败', xhr.status);
    }
})

注意:方案2存在Base64编解码开销,文件体积超过100M时性能会明显下降,大文件下载优先选择方案1。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 18:06:33