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

如何通过Ajax无表单上传PDF,在Servlet中处理字节流并写入文件?

可行性分析与优化方案

这种方案理论上能勉强走通,但存在诸多致命问题,完全不推荐在实际项目中使用,下面我来拆解原因,并给出更合理的替代方案:

为什么不建议这么做?

  • 二进制转JSON字符串的编码失真:JSON是纯文本格式,当你把PDF的字节流直接转成字符串再用JSON.stringify处理时,会因为编码(比如UTF-8)的特性丢失二进制信息。PDF包含大量非文本的二进制数据,转成文本后再解码还原,大概率会导致文件损坏,无法正常打开。
  • 传输与性能损耗:二进制数据转成字符串会显著增大体积(比如用Base64编码会额外增加33%的大小,直接转字符串可能更糟),大文件上传时会占用更多带宽,容易触发请求超时,同时也会增加服务器端的解析压力。
  • 解析复杂度高:Gson本身并不擅长处理二进制数据的序列化/反序列化,你需要手动处理字符串到字节数组的转换,这个过程很容易引入bug,最后写出的文件极大概率是损坏的。

正确的无表单Ajax文件上传方案

实际上,不需要表单也能轻松实现文件上传,推荐使用FormData对象来封装文件,发送multipart/form-data请求,这是浏览器和服务器端都支持的标准文件上传方式,既稳定又高效。

前端JavaScript代码示例

// 假设你通过表格内的文件选择框获取到PDF文件对象
const pdfFile = document.querySelector('.approval-row input[type="file"]').files[0];

// 创建FormData对象,添加文件和审批相关的其他数据(比如行ID)
const formData = new FormData();
formData.append("approvalPdf", pdfFile); // 后端接收的文件参数名
formData.append("approvalRowId", "row-123"); // 关联审批行的ID

// 发送Ajax请求(用fetch或者XMLHttpRequest都可以)
fetch("/approval-upload-servlet", {
  method: "POST",
  body: formData
})
.then(res => res.text())
.then(message => {
  console.log("文件上传完成:", message);
  // 更新表格行的状态,比如标记上传成功
  document.querySelector('.approval-row').classList.add('upload-success');
})
.catch(err => {
  console.error("上传失败:", err);
});

后端Servlet代码示例

首先给Servlet加上@MultipartConfig注解,开启文件上传支持:

@WebServlet("/approval-upload-servlet")
@MultipartConfig(
    fileSizeThreshold = 1024 * 1024 * 2, // 超过2MB的文件直接写入磁盘
    maxFileSize = 1024 * 1024 * 15,      // 单个文件最大15MB
    maxRequestSize = 1024 * 1024 * 50    // 整个请求最大50MB
)
public class ApprovalUploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取上传的文件Part
        Part pdfPart = request.getPart("approvalPdf");
        String rowId = request.getParameter("approvalRowId");
        
        if (pdfPart == null || rowId == null) {
            response.getWriter().write("参数缺失");
            return;
        }

        // 获取原始文件名
        String originalFileName = pdfPart.getSubmittedFileName();
        // 定义文件保存路径(建议用服务器的安全目录,避免直接暴露在web根目录)
        String saveDir = getServletContext().getRealPath("/WEB-INF/approval-pdfs");
        File dir = new File(saveDir);
        if (!dir.exists()) {
            dir.mkdirs(); // 目录不存在则创建
        }
        // 用行ID重命名文件,避免重名覆盖
        String savePath = saveDir + File.separator + rowId + "_" + originalFileName;

        // 写出文件到磁盘
        try (InputStream inputStream = pdfPart.getInputStream();
             FileOutputStream outputStream = new FileOutputStream(savePath)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }

        // 将文件路径关联到你的审批POJO对象,存入数据库或更新流程状态
        // ApprovalPOJO approval = approvalDao.getById(rowId);
        // approval.setPdfFilePath(savePath);
        // approvalDao.update(approval);

        response.getWriter().write("PDF上传成功,已关联到审批行:" + rowId);
    }
}

方案优势

  • 无编码失真:直接传输二进制文件流,完全保留原始文件的完整性
  • 高效传输:没有额外的编码膨胀,大文件上传更稳定
  • 标准兼容:Servlet的MultipartConfig和浏览器的FormData都是标准API,兼容性好,维护成本低

内容的提问来源于stack exchange,提问作者Tousik Ghosh Dastidar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 12:17:49