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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 11:43:29