Part
接口或第三方库如Apache Commons FileUpload处理HTTP请求流;下载文件则通过设置响应头Content-Disposition
并读取文件流写入响应输出流完成,核心步骤包括解析请求、读写文件流和异常处理。在Web应用中,文件上传与下载是高频需求,本文详细讲解Java实现方案,涵盖核心API、安全实践及完整代码示例,帮助开发者构建健壮的文件传输功能。
环境准备
- 基础环境:JDK 8+、Servlet 3.0+(支持
Part
接口)、Tomcat 9+ - 可选库:Apache Commons FileUpload(传统表单上传)
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.5</version> </dependency>
文件上传实现
方案1:Servlet API原生实现(推荐)
@WebServlet("/upload") @MultipartConfig(maxFileSize = 1024 * 1024 * 10) // 限制10MB public class FileUploadServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) { try { Part filePart = request.getPart("file"); // "file"为表单字段名 String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); InputStream fileContent = filePart.getInputStream(); // 存储到服务器(示例路径) Files.copy(fileContent, Paths.get("/uploads/" + fileName)); response.getWriter().print("上传成功: " + fileName); } catch (Exception e) { response.sendError(500, "上传失败: " + e.getMessage()); } } }
关键参数说明:
@MultipartConfig
:配置上传限制maxFileSize
:单文件最大字节数maxRequestSize
:总请求最大字节数location
:临时存储目录
方案2:Apache Commons FileUpload(兼容旧版)
DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); upload.setSizeMax(10 * 1024 * 1024); // 10MB限制 List<FileItem> items = upload.parseRequest(request); for (FileItem item : items) { if (!item.isFormField()) { // 跳过普通表单字段 String fileName = new File(item.getName()).getName(); File storeFile = new File("/uploads/" + fileName); item.write(storeFile); // 保存文件 } }
文件下载实现
@WebServlet("/download") public class FileDownloadServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) { String fileName = request.getParameter("filename"); Path filePath = Paths.get("/uploads/", fileName); try (InputStream in = Files.newInputStream(filePath); OutputStream out = response.getOutputStream()) { // 设置响应头 response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename="" + fileName + """); // 流复制 byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } catch (IOException e) { response.sendError(404, "文件不存在"); } } }
响应头解析:
Content-Type: application/octet-stream
:强制浏览器下载Content-Disposition: attachment
:指定默认文件名
安全实践
-
文件类型校验
使用文件头(Magic Number)而非扩展名:byte[] header = Files.readAllBytes(filePath); if (!Arrays.equals(header, 0, 4, new byte[]{0x25, 0x50, 0x44, 0x46}, 0, 4)) { // PDF示例 throw new SecurityException("非法文件类型"); }
-
路径安全
- 使用
Paths.get("/safe_dir/", fileName).normalize()
防止路径遍历攻击 - 禁止用户自定义存储路径
- 使用
-
大小限制
- 服务端双重校验:前端表单限制 + 后端
@MultipartConfig
或setSizeMax()
- 服务端双重校验:前端表单限制 + 后端
-
重命名策略
String safeName = UUID.randomUUID() + "_" + fileName; // 避免文件名冲突
前端表单示例
<!-- 上传表单 --> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="file" accept=".pdf,.jpg,.png"> <!-- 前端过滤 --> <button type="submit">上传</button> </form> <!-- 下载链接 --> <a href="/download?filename=report.pdf">下载报告</a>
- 上传:优先使用Servlet 3.0+的
Part
接口,简洁高效 - 下载:重点控制响应头与流复制
- 安全:类型校验、路径规范、大小限制缺一不可
- 扩展建议:
- 大文件上传:分片传输(如Dropzone.js + 后台合并)
- 云存储:集成AWS S3或阿里云OSS SDK
引用说明:
- Servlet 3.0规范:Oracle官方文档
- Apache Commons FileUpload:Apache 2.0协议开源库
- 安全实践参考:OWASP文件上传防护指南
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/18836.html