通过XMLHttpRequest从JSON获取文件后无法打开的问题排查
问题:通过XMLHttpRequest获取JSON接口返回的文件后无法正常打开
尝试通过XMLHttpRequest从JSON接口获取文件,下载后的文件无法正常打开。数据库中存储的文件本身无异常(在C#中可正常保存并打开),问题仅出在JavaScript接收文件的环节。
前端JavaScript代码
var xmlHttpRequest = new XMLHttpRequest(); if (!window.XMLHttpRequest) { xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } let formData = new FormData(); formData.append('request.Id', window.$(this).attr('data')); xmlHttpRequest.open("POST", 'Url', true); xmlHttpRequest.onloadend = function () { let response = ParseJson(xmlHttpRequest.responseText); alert(response.Result); let blob = new Blob([response.File], { type: response.MimeType }); let a = document.createElement('a'); a.href = window.URL.createObjectURL(blob); a.download = response.FileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(a.href); } xmlHttpRequest.send(formData);
后端C# Controller代码
public IActionResult GetFileDownload(GetFileDownloadRequest request) { //Get byte[] from database return Json(new GetFileDownload() { Result = "OK", FileName = file.Name ?? "", MimeType = Path.GetExtension(file.Name).GetMimeType(), File = file.File }); }
问题原因及解决方案
核心问题
后端返回JSON时,byte[]类型的File会被自动序列化为Base64字符串。而前端直接将这个字符串作为字节内容创建Blob,导致文件实际存储的是Base64文本,而非原始二进制数据,因此无法正常打开。
方案一:前端解码Base64字符串(无需修改后端)
修改前端onloadend中的Blob创建逻辑,将Base64字符串解码为原始字节数组:
xmlHttpRequest.onloadend = function () { let response = ParseJson(xmlHttpRequest.responseText); alert(response.Result); // 解码Base64字符串为Uint8Array const byteCharacters = atob(response.File); const byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); let blob = new Blob([byteArray], { type: response.MimeType }); let a = document.createElement('a'); a.href = window.URL.createObjectURL(blob); a.download = response.FileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(a.href); }
方案二:后端直接返回文件流(更高效,推荐)
如果不需要同时返回其他JSON数据,可以修改后端直接返回文件流,前端直接接收Blob:
修改后端代码
public IActionResult GetFileDownload(GetFileDownloadRequest request) { //Get byte[] from database // 直接返回文件流,自动处理MIME类型和文件名 return File(file.File, file.MimeType, file.Name); }
修改前端代码
var xmlHttpRequest = new XMLHttpRequest(); if (!window.XMLHttpRequest) { xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } let formData = new FormData(); formData.append('request.Id', window.$(this).attr('data')); xmlHttpRequest.open("POST", 'Url', true); // 设置响应类型为blob xmlHttpRequest.responseType = 'blob'; xmlHttpRequest.onloadend = function () { // 直接获取Blob对象 let blob = xmlHttpRequest.response; let a = document.createElement('a'); a.href = window.URL.createObjectURL(blob); // 从响应头解析文件名(后端返回File时自动设置Content-Disposition) const contentDisposition = xmlHttpRequest.getResponseHeader('Content-Disposition'); let fileName = ''; if (contentDisposition) { const matches = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/); if (matches != null && matches[1]) { fileName = decodeURIComponent(matches[1].replace(/['"]/g, '')); } } a.download = fileName || response.FileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(a.href); } xmlHttpRequest.send(formData);
内容的提问来源于stack exchange,提问作者Mohamad Hasan Salmaaniyaan
相关产品推荐
相关产品推荐

