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

.NET服务端返回翻译文件至客户端后无法正常读取问题

问题分析与解决方案

问题根源

你的API错误地将FileContentResult序列化为JSON对象返回,客户端拿到的fileContents是Base64编码的字符串。直接用这个字符串创建Blob时,相当于把Base64文本的字节写入文件,而非解码后的原始文件字节,导致Word无法识别损坏的文件结构。


1. 修正控制器代码

核心是直接返回文件结果而非JSON包装的对象,让ASP.NET Core框架自动处理文件流响应:

[HttpPost]
[Route("TranslateDocument/{inputLanguage}/{outputLanguage}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Produces("application/vnd.openxmlformats-officedocument.wordprocessingml.document")]
public async Task<IActionResult> TranslateDocument(string inputLanguage, string outputLanguage, [FromForm] IFormFile file)
{
    try
    {
        FileContentResult result = await _deeplService.TranslateFile(inputLanguage, outputLanguage, file);
        // 直接返回FileContentResult,框架会生成正确的文件流响应
        return result;
    }
    catch (Exception ex)
    {
        return ManageExceptions(ex);
    }
}
  • 移除了ActionResult<FileInfo>的错误返回类型,改为IActionResult以支持直接返回文件结果
  • 添加[Produces]标注明确响应的Content-Type,帮助客户端识别文件类型

2. 优化服务层(可选,职责更清晰)

让服务层返回原始文件字节和文件名,由控制器构建文件结果:

public async Task<(byte[] FileBytes, string FileName)> TranslateFile(string inputLang, string outputLang, IFormFile file)
{
    // 文件翻译逻辑...

    byte[] bytes = System.IO.File.ReadAllBytes("localpath from doc");
    // 生成带标识的文件名,方便用户识别
    string fileName = $"{Path.GetFileNameWithoutExtension(file.FileName)}_translated_{outputLang}.docx";
    return (bytes, fileName);
}

对应控制器修改:

public async Task<IActionResult> TranslateDocument(string inputLanguage, string outputLanguage, [FromForm] IFormFile file)
{
    try
    {
        var (fileBytes, fileName) = await _deeplService.TranslateFile(inputLanguage, outputLanguage, file);
        return File(fileBytes, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", fileName);
    }
    catch (Exception ex)
    {
        return ManageExceptions(ex);
    }
}

3. 修正客户端代码

现在API直接返回文件流,客户端需要以Blob形式接收响应,而非解析JSON:

Fetch示例

const formData = new FormData();
formData.append('file', yourFileElement.files[0]);

fetch(`/TranslateDocument/${inputLang}/${outputLang}`, {
    method: 'POST',
    body: formData
})
.then(response => response.blob())
.then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    // 从响应头获取自动生成的文件名(若API返回)
    const contentDisposition = response.headers.get('Content-Disposition');
    a.download = contentDisposition?.match(/filename="?([^"]+)"?/)?.[1] || 'translated_document.docx';
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    document.body.removeChild(a);
})
.catch(err => console.error('下载失败:', err));

Axios示例

const formData = new FormData();
formData.append('file', yourFileElement.files[0]);

axios.post(`/TranslateDocument/${inputLang}/${outputLang}`, formData, {
    responseType: 'blob'
})
.then(res => {
    const blob = new Blob([res.data], { type: res.headers['content-type'] });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    // 解析响应头中的文件名
    const contentDisposition = res.headers['content-disposition'];
    a.download = contentDisposition?.match(/filename="?([^"]+)"?/)?.[1] || 'translated_document.docx';
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    document.body.removeChild(a);
})
.catch(err => console.error('下载失败:', err));

备选方案(不推荐,仅兼容旧逻辑)

如果必须保留JSON返回方式,客户端需要先将Base64字符串解码为原始字节再创建Blob:

// 仅当API仍返回JSON时使用
const byteChars = atob(response.fileContents);
const byteNumbers = new Array(byteChars.length);
for (let i = 0; i < byteChars.length; i++) {
    byteNumbers[i] = byteChars.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: response.contentType });
// 后续下载逻辑同上

内容的提问来源于stack exchange,提问作者Emili Bellot Pulido

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:45:31