如何在JavaScript中验证服务器返回结果是否为有效文档?
问题:如何在JavaScript中验证后端返回的文档是否有效
场景与代码片段
客户端通过Ajax调用Java后端获取PDF/Word文档,后端直接输出文件字节数组。正常情况下可完成下载,但异常时会返回非有效文档,需在客户端验证返回结果是否为指定的有效文档(支持.pdf、.doc、.docx)。当前返回结果始终为String类型。
客户端JavaScript代码
$.ajax({ type: "get", url: urlStringCustomerInvoices, contentType: "application/json; charset=utf-8", dataType: "text", success: function (result) { if (result.length > 0) { // 待补充验证逻辑 } } });
后端Java代码
OutputStream os = response.getOutputStream(); bos = new BufferedOutputStream(os); bos.write(bytes); bos.flush(); os.flush(); os.close();
当前现状
- 返回结果类型固定为
String(Object.prototype.toString.call(result)输出[object String]) - 有效PDF的
result.length为66402,但仅靠长度无法判断文档有效性
解决方案:通过文件签名(Magic Number)验证
不同类型的文档开头有固定的字节特征(文件签名),这是判断文件类型最可靠的方式。核心思路是先把返回的字符串还原为字节数组,再匹配对应文档的签名。
1. 优化Ajax接收方式(推荐)
将dataType改为arraybuffer,直接接收二进制数据,避免字符串转换带来的编码丢失问题:
$.ajax({ type: "get", url: urlStringCustomerInvoices, contentType: "application/json; charset=utf-8", dataType: 'arraybuffer', // 直接接收二进制缓冲区 success: function (result) { const uint8Arr = new Uint8Array(result); // 后续验证逻辑写在这里 } });
如果必须保留dataType: "text",可以先把字符串转成字节数组:
function stringToUint8Array(str) { return new TextEncoder().encode(str); } // 在success回调中调用: const uint8Arr = stringToUint8Array(result);
2. 编写验证函数
匹配三种文档的固定签名:
function isValidDocument(uint8Arr) { // PDF签名:%PDF- 对应的ASCII字节 const pdfSig = new Uint8Array([37, 80, 68, 70, 45]); // DOC签名:复合文档格式的固定开头 const docSig = new Uint8Array([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]); // DOCX签名:ZIP压缩包开头(DOCX本质是ZIP文件) const docxSig = new Uint8Array([0x50, 0x4B, 0x03, 0x04]); // 逐个匹配签名 const checkMatch = (sig) => { if (uint8Arr.length < sig.length) return false; return sig.every((byte, index) => uint8Arr[index] === byte); }; return checkMatch(pdfSig) || checkMatch(docSig) || checkMatch(docxSig); }
3. 整合完整逻辑
把验证和下载/错误处理结合:
$.ajax({ type: "get", url: urlStringCustomerInvoices, contentType: "application/json; charset=utf-8", dataType: 'arraybuffer', success: function (result) { const uint8Arr = new Uint8Array(result); if (isValidDocument(uint8Arr)) { // 有效文档,触发下载 const blob = new Blob([uint8Arr], { type: getDocMimeType(uint8Arr) }); const downloadUrl = URL.createObjectURL(blob); const aTag = document.createElement('a'); aTag.href = downloadUrl; aTag.download = `document.${getDocExtension(uint8Arr)}`; aTag.click(); URL.revokeObjectURL(downloadUrl); } else { // 无效文档,尝试解析错误信息 const errorMsg = new TextDecoder('utf-8').decode(uint8Arr); console.error('返回的不是有效文档,错误信息:', errorMsg); // 这里可以添加前端错误提示逻辑 } }, error: function(xhr) { console.error('请求失败:', xhr.statusText); } }); // 辅助函数:获取文档MIME类型 function getDocMimeType(uint8Arr) { if (new Uint8Array([37,80,68,70,45]).every((b,i) => uint8Arr[i] === b)) { return 'application/pdf'; } else if (new Uint8Array([0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1]).every((b,i) => uint8Arr[i] === b)) { return 'application/msword'; } else if (new Uint8Array([0x50,0x4B,0x03,0x04]).every((b,i) => uint8Arr[i] === b)) { return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; } return 'application/octet-stream'; } // 辅助函数:获取文档扩展名 function getDocExtension(uint8Arr) { const mimeType = getDocMimeType(uint8Arr); switch(mimeType) { case 'application/pdf': return 'pdf'; case 'application/msword': return 'doc'; default: return 'docx'; } }
内容的提问来源于stack exchange,提问作者Richard
相关产品推荐
相关产品推荐

