
本文介绍在java web服务端(如jax-rs或servlet环境)中,安全、高效地解析 multipart/form-data 请求以提取上传文件的正确方法,推荐使用 apache commons fileupload 库,避免手动解析带来的边界错误与编码风险。
在处理文件上传时,绝不要手动解析 multipart/form-data 流——其格式包含复杂的分隔符(boundary)、头字段、编码转换和潜在的二进制/文本混合内容。您当前服务端代码中用 BufferedReader 读取 request.getInputStream() 的方式会严重破坏原始字节流:BufferedReader 基于字符解码(默认平台编码),而文件内容(如 Excel 的 .xlsx)是二进制数据,会导致乱码、截断甚至解析失败。
✅ 正确做法:使用成熟稳定的 Apache Commons FileUpload 库(配合 commons-io)。它专为解析 multipart 请求设计,能自动识别表单字段与文件项、处理编码、支持内存/磁盘双缓冲,并兼容 Servlet 标准。
✅ 服务端集成步骤(以 JAX-RS + Servlet 容器为例)
-
添加依赖(Maven):
commons-fileupload commons-fileupload 1.5 commons-io commons-io 2.11.0 -
修改服务端方法(完整示例):
@POST @Path("/createRequest") @Consumes(MediaType.MULTIPART_FORM_DATA) // 显式声明支持 multipart public Response createRequest(@Context HttpServletRequest request) { try { DiskFileItemFactory factory = new DiskFileItemFactory(); // 可选:设置内存阈值(如 4MB),超限时自动写入临时文件 factory.setSizeThreshold(4 * 1024 * 1024); // 可选:指定临时文件目录(生产环境建议显式设置) factory.setRepository(new File(System.getProperty("java.io.tmpdir"))); ServletFileUpload upload = new ServletFileUpload(factory); upload.setHeaderEncoding("UTF-8"); // 关键!解决中文文件名乱码 upload.setFileSizeMax(10 * 1024 * 1024); // 单文件最大 10MB upload.setSizeMax(20 * 1024 * 1024); // 总请求最大 20MB Listitems = upload.parseRequest(request); for (FileItem item : items) { if (item.isFormField()) { // 处理普通表单字段(如 id, lastModified) String fieldName = item.getFieldName(); String value = item.getString("UTF-8"); System.out.println("Field: " + fieldName + " = " + value); } else { // 处理上传文件 String fileName = item.getName(); if (fileName != null && !fileName.trim().isEmpty()) { // 注意:IE 会传完整路径(如 c:\temp\file.xlsx),需截取文件名 fileName = Paths.get(fileName).getFileName().toString(); InputStream fileContent = item.getInputStream(); // ✅ 安全保存文件(示例:存到服务器指定目录) Path targetPath = Paths.get("/opt/uploads/", fileName); Files.createDirectories(targetPath.getParent()); Files.copy(fileContent, targetPath, StandardCopyOption.REPLACE_EXISTING); System.out.println("Uploaded: " + fileName + " (" + item.getSize() + " bytes)"); } } } return Response.ok().entity("File uploaded successfully").build(); } catch (FileUploadException e) { return Response.status(Response.Status.BAD_REQUEST) .entity("Invalid file upload: " + e.getMessage()).build(); } catch (IOException e) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Server error during upload").build(); } }
⚠️ 重要注意事项
- @Consumes(MediaType.MULTIPART_FORM_DATA) 是良好实践:明确告知框架该端点接受 multipart 请求(部分容器如 Jersey 会据此启用预处理)。
- 务必调用 item.getName() 获取原始文件名,而非 item.getFieldName():后者返回的是 中的字段名(如 "file"),而 getName() 才是客户端提交的真实文件名。
- 中文文件名必须设 upload.setHeaderEncoding("UTF-8"):否则 item.getName() 返回乱码(尤其在 Tomcat 8+ 默认 ISO-8859-1 下)。
- 始终校验 item.getName() 是否为空或仅含路径分隔符:防御恶意构造的空文件名或路径遍历攻击(如 ../../etc/passwd)。
- 生产环境必须设置 setFileSizeMax() 和 setSizeMax():防止拒绝服务攻击(DoS)。
- 临时文件清理:FileItem 在方法结束后自动清理,无需手动删除;若需长期保留,请显式 item.write(new File(...)) 并确保异常时清理。
通过 Apache Commons FileUpload,您可将复杂、易错的 multipart 解析逻辑交由经过广泛验证的库处理,专注业务逻辑,大幅提升代码健壮性与可维护性。










