java rest文件下载需设content-disposition和content-type响应头,用responseentity返回本地文件或httpservletresponse动态生成内容,注意中文名编码与路径遍历防护。

在 Java 的 REST 接口中实现文件下载,核心是正确设置响应头(尤其是 Content-Disposition 和 Content-Type),并把文件内容以流方式写入 HTTP 响应体。Spring Boot(基于 Spring MVC)是最常用场景,下面以它为例说明关键步骤和注意事项。
设置正确的响应头和返回类型
不要用 @ResponseBody 或直接返回字符串/对象;应使用 ResponseEntity<resource></resource> 或直接操作 HttpServletResponse。推荐前者,更符合 REST 风格且自动处理部分内容。
-
必须设置
Content-Disposition:例如attachment; filename="report.pdf",告诉浏览器这是附件、要触发下载,并指定默认保存名(注意对中文文件名做 UTF-8 编码) -
合理设置
Content-Type:可用MediaType.APPLICATION_OCTET_STREAM(通用二进制流),或根据文件后缀推断(如Files.probeContentType(path)) -
建议设置
Content-Length:提升体验和稳定性,尤其对大文件;可通过resource.contentLength()获取
用 ResponseEntity + Resource 返回本地文件
适用于文件存在服务器本地磁盘或 classpath 中。Spring 提供了 FileSystemResource、ClassPathResource、UrlResource 等封装。
@GetMapping("/download/{filename}")
public ResponseEntity<resource> downloadFile(@PathVariable String filename) throws IOException {
Path filePath = Paths.get("uploads/", filename);
Resource resource = new UrlResource(filePath.toUri());
if (!resource.exists()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "文件不存在");
}
String contentType = Files.probeContentType(filePath);
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
// 处理中文文件名(RFC 5987 格式)
String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8)
.replace("+", "%20");
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename*=UTF-8''" + encodedFilename)
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
.body(resource);
}</resource>
动态生成并下载(如导出 Excel/PDF)
不依赖已有文件,而是运行时生成字节数组或写入输出流。此时更适合用 HttpServletResponse 手动控制。
- 调用
response.getOutputStream()或response.getWriter()(后者仅限文本) - 先设好响应头,再写入内容,最后
flush()和close() - 避免在 Controller 方法中返回值(即方法返回
void),防止 Spring 尝试序列化
@GetMapping("/export/excel")
public void exportExcel(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition",
"attachment; filename*=UTF-8''" + URLEncoder.encode("用户报表.xlsx", "UTF-8"));
try (OutputStream out = response.getOutputStream()) {
// 假设 generateExcelBytes() 返回 byte[]
byte[] data = generateExcelBytes();
out.write(data);
out.flush();
}
}
注意文件名安全与路径遍历防护
用户传入的 filename 参数绝不能直接拼接路径,否则可能被用于读取任意系统文件(如 ../../etc/passwd)。
- 校验文件名是否只含合法字符(如字母、数字、下划线、短横线、点)
- 使用
Paths.get(filename).getFileName().toString()提取纯文件名,丢弃路径部分 - 限定文件存放目录,用
Paths.get(uploadDir, safeFilename)构造绝对路径,再检查是否仍在该目录内(toRealPath()+ startsWith)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











