.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
相关产品推荐
相关产品推荐

