Java服务端如何解析multipart/form-data请求中的文件?
服务端解析Multipart文件的解决方案
你的客户端已经正确发送了multipart/form-data格式的文件,但当前服务端直接通过BufferedReader读取输入流的方式无法解析这种带边界分隔的二进制混合数据,以下是两种可行的解决方法:
方案1:使用JAX-RS原生@FormDataParam注解(推荐)
因为你的服务端基于JAX-RS(从@POST、@Path注解判断),可以直接利用框架的Multipart支持来简化文件接收:
步骤1:引入Multipart依赖
如果用Jersey作为JAX-RS实现,添加Maven依赖:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-multipart</artifactId> <version>对应你的Jersey版本</version> </dependency>
同时在你的JAX-RS应用类中注册MultipartFeature:
public class YourApplication extends ResourceConfig { public YourApplication() { register(MultipartFeature.class); // 其他资源注册 } }
步骤2:修改服务端代码
直接通过注解获取文件流、文件信息,以及请求头/表单参数:
import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.Response; import javax.ws.rs.HeaderParam; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @POST @Path("/createRequest") @Consumes("multipart/form-data") public Response createRequest( // 接收上传的文件流和文件元数据 @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition fileDisposition, // 从请求头获取你传递的id和lastModified @HeaderParam("id") String requestId, @HeaderParam("lastModified") String lastUpdated) { try { // 获取客户端传递的文件名 String fileName = fileDisposition.getFileName(); // 将文件保存到指定路径 Files.copy(fileInputStream, Paths.get("你的保存目录/" + fileName), StandardCopyOption.REPLACE_EXISTING); // 这里可以处理requestId和lastUpdated参数 System.out.println("请求ID:" + requestId); System.out.println("最后修改时间:" + lastUpdated); return Response.ok("文件接收成功:" + fileName).build(); } catch (Exception e) { e.printStackTrace(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("文件接收失败").build(); } }
方案2:手动解析Multipart数据(不推荐)
如果不想依赖框架的Multipart支持,可以手动根据请求头中的边界值拆分请求体,但这种方式需要处理二进制数据的编码问题,实现繁琐:
import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.Response; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Context; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import org.apache.commons.io.IOUtils; @POST @Path("/createRequest") public Response createRequest(@Context HttpServletRequest request) { try { String contentType = request.getContentType(); if (!contentType.startsWith("multipart/form-data")) { return Response.status(Response.Status.BAD_REQUEST).entity("非法请求格式").build(); } // 提取boundary值 String boundary = "--" + contentType.split("boundary=")[1]; InputStream inputStream = request.getInputStream(); byte[] requestBytes = IOUtils.toByteArray(inputStream); // 拆分multipart各部分 String[] parts = new String(requestBytes, "UTF-8").split(boundary); for (String part : parts) { if (part.contains("Content-Disposition: form-data; name=\"file\"")) { // 定位文件二进制数据的起始和结束位置 int start = part.indexOf("\r\n\r\n") + 4; int end = part.lastIndexOf("\r\n--"); if (start > 0 && end > start) { // 提取文件字节并保存 byte[] fileBytes = new byte[end - start]; System.arraycopy(requestBytes, start, fileBytes, 0, end - start); Files.write(Paths.get("你的保存目录/testFile.xlsx"), fileBytes); break; } } } return Response.ok("文件接收成功").build(); } catch (Exception e) { e.printStackTrace(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } }
客户端代码修正
当前客户端缺少Multipart请求的结束边界标记,需要在写入文件后添加:
// 写完文件字节后添加 outStream.writeBytes(crlf); outStream.writeBytes(twoHyphens + boundary + twoHyphens + crlf); // 结束边界
否则服务端可能会认为请求不完整。
内容的提问来源于stack exchange,提问作者Amol
相关产品推荐
相关产品推荐

