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

使用PDFAnnotate和jsPDF生成PDF后如何上传保存到PHP服务端

问题排查与修复方案

核心问题定位

前端问题

  • 原生XHR提交时,xhr.send()传入的是字符串'pdf',而非你构造好的formData对象,根本没有提交PDF文件数据
  • 如果改用jQuery ajax提交FormData,需要额外设置两个参数避免jQuery自动处理数据导致上传失败

后端问题

  • 即使前端正确提交了文件,后端逻辑也错误:文件数据存储在$_FILES全局变量中,你判断了$_FILES['pdf']存在,却从$_POST里取数据,必然取不到有效内容
  • 没有验证存储目录的存在性和写入权限,即使拿到文件也可能写入失败

排查步骤

  • 打开浏览器开发者工具的「网络」面板,触发上传后查看对应POST请求:
    • 检查请求头Content-Type是否为multipart/form-data且带有boundary参数
    • 检查请求载荷中是否包含PDF的二进制数据
    • 查看接口返回的错误信息,确认是触发了no data异常还是其他报错
  • 服务端临时添加调试代码,输出var_dump($_FILES, $_POST),查看实际接收到的数据结构
  • 检查服务端testa/pdf目录是否存在,对应进程是否有该目录的写入权限

修复代码

前端(原生XHR版本)

PDFAnnotate.prototype.savePdf = function() {
    var inst = this;
    var doc = new jspdf.jsPDF();
    inst.fabricObjects.forEach(function(fabricObj, index) {
        if (index != 0) {
            doc.addPage();
            doc.setPage(index + 1);
        }
        doc.addImage(
            fabricObj.toDataURL({
                format: 'png'
            }),
            inst.pageImageCompression == "NONE" ? "PNG" : "JPEG",
            0,
            0,
            doc.internal.pageSize.getWidth(),
            doc.internal.pageSize.getHeight(),
            `page-${index + 1}`,
            ["FAST", "MEDIUM", "SLOW"].indexOf(inst.pageImageCompression) >= 0 ?
            inst.pageImageCompression :
            undefined
        );
        if (index === inst.fabricObjects.length - 1) {
            var blob = doc.output('blob');
            var formData = new FormData();
            formData.append('pdf', blob, 'generated.pdf'); // 第三个参数可选,指定文件名
            
            var xhr = new XMLHttpRequest();
            xhr.open('POST', baseurl + "wo/replaceFile", true);
            // 不要手动设置Content-Type,浏览器会自动生成带boundary的正确值
            xhr.send(formData); // 这里传构造好的formData,不是字符串'pdf'
        }
    })
}

前端(jQuery Ajax版本,符合前期调研选型)

// 替换上述代码中XHR部分即可
$.ajax({
    url: baseurl + "wo/replaceFile",
    type: 'POST',
    data: formData,
    processData: false, // 禁止jQuery自动序列化FormData
    contentType: false, // 禁止jQuery自动设置Content-Type
    success: function(res) {
        console.log('上传成功', res)
    },
    error: function(err) {
        console.log('上传失败', err)
    }
})

后端修复代码

if(!empty($_FILES['pdf'])) {
    // 从$_FILES取临时文件路径
    $tmpPath = $_FILES['pdf']['tmp_name'];
    $fname = "test.pdf";
    $saveDir = "testa/pdf/";
    // 先判断目录不存在就创建
    if (!is_dir($saveDir)) {
        mkdir($saveDir, 0755, true);
    }
    $savePath = $saveDir . $fname;
    // 用move_uploaded_file移动临时文件更安全,比fwrite可靠
    if (move_uploaded_file($tmpPath, $savePath)) {
        echo json_encode(['code' => 0, 'msg' => '保存成功']);
    } else {
        throw new Exception("文件保存失败,请检查目录权限");
    }
} else {
    throw new Exception("no data");
}
?>

内容的提问来源于stack exchange,提问作者siayam rumput

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 20:15:01