Java文件上传与下载的基本语法操作

Java Web文件上传核心是MultipartFile接口,需用transferTo()安全落盘,禁用getBytes()和手动流操作;下载须设Content-Disposition和Content-Type响应头;生产环境应使用独立路径或对象存储。

Java Web 中文件上传的核心是 MultipartFile

Spring Boot 项目里,MultipartFile 是处理 HTTP 文件上传事实上的标准接口。它不是 Java 标准库类,而是 Spring MVC 提供的抽象,底层依赖 Servlet 3.0+ 的 Part 接口。你不能直接 new 它,必须由框架在接收请求时自动注入。

常见错误:试图用 File file = multipartFile.getResource().getFile() 拿到真实路径——这在多数部署环境(如 jar 包运行、Docker)会抛 FileNotFoundException,因为文件可能根本没落地为磁盘文件,而是在内存中流转。

  • 上传接口必须使用 POST 方法,且 Content-Type 必须是 multipart/form-data
  • Controller 方法参数要加 @RequestParam("file") MultipartFile file,其中 "file" 必须和 HTML 表单中 name 属性一致
  • 务必检查 file.isEmpty(),否则用户未选文件就提交会导致空指针或异常
  • 大文件上传需提前配置:spring.servlet.multipart.max-file-size=10MBmax-request-size=10MB(application.yml)

保存上传文件别硬写 FileOutputStream,优先用 transferTo()

MultipartFile.transferTo(File dest) 是最安全、最简洁的落盘方式。它内部自动处理内存/临时文件切换,兼容不同容器(Tomcat、Undertow、Jetty),也避免了手动流操作容易遗漏的 close 问题。

别用 file.getBytes() 全量读入内存再写——上传 500MB 文件会直接 OOM;也别用 file.getInputStream() + FileOutputStream 手动 copy——出错时流没关干净,临时文件可能残留。

Path uploadDir = Paths.get("uploads");
Files.createDirectories(uploa

dDir); File dest = uploadDir.resolve(file.getOriginalFilename()); file.transferTo(dest); // 一行搞定,自动清理临时资源

文件下载必须设对 Content-DispositionContent-Type

浏览器能否触发下载、文件名是否正确、中文名是否乱码,全靠两个响应头:

  • Content-Disposition: attachment; filename="abc.pdf" —— 触发下载行为;filename* 用于支持 UTF-8 编码的中文名(如 filename*=UTF-8''%E6%96%87%E4%BB%B6.pdf
  • Content-Type 决定浏览器是否尝试在线打开。不确定类型时用 application/octet-stream 最稳妥
  • 千万别用 response.getOutputStream().write(bytes) 直接写,漏设 header 会导致文件名丢失或页面显示乱码内容
String fileName = "报告_2025.pdf";
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", 
    "attachment; filename*=UTF-8''" + URLEncoder.encode(fileName, "UTF-8"));
Files.copy(Paths.get("data/", fileName), response.getOutputStream());

生产环境绕不开的路径与权限问题

本地开发时 uploads/ 放项目根目录没问题,但上线后:Linux 上应用通常以非 root 用户运行,对 /var/www/opt/app 下目录默认无写权限;Windows 服务也可能因 UAC 被拦截。更关键的是,上传目录绝不能放在 Web 根路径下(如 src/main/resources/static/uploads),否则用户可通过 URL 直接访问,造成敏感文件泄露。

解决方案只有两个:一是用独立可写路径(如 /data/app/uploads),并在启动脚本里 chown appuser:appgroup /data/app;二是走对象存储(OSS/S3),上传后返回外链,彻底规避本地文件系统权限和容量问题。

容易被忽略的一点:Files.createDirectories(path) 只创建末级目录,如果父目录不存在且无权限,它不会报错,而是静默失败——必须在创建后立刻 Files.exists(path) 校验。