spring文件下載的四種方式及對比分析
更新時間:2025年08月13日 09:40:31 作者:言不由衷煦
本文對比spring文件下載的四種方式,每種方式結(jié)合實例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
方式一:通過ResponseEntity 方式來下載
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/1")
public ResponseEntity<Resource> downloadFile01(HttpServletRequest request) {
try {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
// 檢查資源是否存在
if (resource.exists() || resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
log.error("文件模板下載失??!", e);
return ResponseEntity.internalServerError().build();
}
}
/**
* 根據(jù)瀏覽器類型編碼文件名
* @param request 請求對象
* @param fileName 原始文件名
* @return 編碼后的文件名
*/
private static String encodeFileName(HttpServletRequest request, String fileName) {
String userAgent = request.getHeader("User-Agent");
// 處理IE及Edge瀏覽器
if (userAgent.contains("MSIE") || userAgent.contains("Trident") || userAgent.contains("Edge")) {
return cn.hutool.core.net.URLEncoder.createDefault().encode(fileName, StandardCharsets.UTF_8);
}
// 其他瀏覽器使用ISO-8859-1編碼
return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
}方式二:通過ResponseEntity 方式來下載
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/2")
public ResponseEntity<StreamingResponseBody> downloadFile02(HttpServletRequest request) {
try {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
StreamingResponseBody body = outputStream -> {
FileUtil.writeToStream(resource.getFile(), outputStream);
};
// 檢查資源是否存在
if (resource.exists() || resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
// 省略 encodeFileName 方法
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(body);
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
log.error("文件模板下載失?。?, e);
return ResponseEntity.internalServerError().build();
}
}方式三:通過Servlet原生下載
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/3")
public void downloadFile03(HttpServletRequest request, HttpServletResponse response) throws IOException {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
// 設(shè)置響應(yīng)頭
response.setContentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());
response.setHeader("Content-Disposition", "attachment; filename=" + encodeFileName(request, resource.getFilename()));
// 這里是關(guān)鍵,如果設(shè)置 response.setContentLength() 則下載的文件只能是 50 多兆
response.setContentLengthLong(resource.contentLength());
FileUtil.writeToStream(resource.getFile(), response.getOutputStream());
}
方式四:通過ResponseEntity<byte[]> 方式來下載
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/4")
public ResponseEntity<byte[]> downloadFile04(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
// 檢查資源是否存在
if (resource.exists() || resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(FileUtil.readBytes(resource.getFile()));
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
log.error("文件模板下載失敗!", e);
return ResponseEntity.internalServerError().build();
}
}
四種下載方式的對比
1、核心特性對比
| 方式 | 內(nèi)存占用 | 適用場景 | 流式支持 | 資源管理 | 代碼復(fù)雜度 |
|---|---|---|---|---|---|
ResponseEntity<byte[]> | 高(全量加載) | 小文件(<10MB)如配置文件、圖片 | ? | 自動釋放內(nèi)存 | 低(簡單封裝) |
ResponseEntity<Resource> | 中(按需加載) | 動態(tài)資源(如JAR內(nèi)文件、數(shù)據(jù)庫Blob) | ?(需手動關(guān)閉流) | 需顯式關(guān)閉InputStream | 中(需處理流) |
ResponseEntity<StreamingResponseBody> | 極低(分塊傳輸) | 超大文件(視頻、日志等) | ?(自動分塊) | 自動處理流生命周期 | 高(需分塊邏輯) |
Servlet原生下載 | 低(手動控制) | 需要精細(xì)控制響應(yīng)頭/流的場景 | ?(手動實現(xiàn)) | 需手動關(guān)閉流 | 高(冗余代碼) |
2、典型場景推薦
- 快速小文件下載:優(yōu)先使用
ResponseEntity<byte[]>,直接返回字節(jié)數(shù)組,無需流控制。 - 動態(tài)資源或加密文件:選擇
ResponseEntity<Resource>,靈活處理輸入流。 - **超大文件(GB級)**:必須用
StreamingResponseBody分塊傳輸,避免OOM。 - 兼容舊系統(tǒng)或特殊需求:
Servlet原生下載(如需要自定義響應(yīng)頭邏輯)
完整的代碼
import cn.hutool.core.io.FileUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* description
*
* @author lijin
* @since 2025-08-12 14:05
*/
@Slf4j
@Controller
@RequestMapping("/cycle-data/document01")
public class DownloadController {
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/1")
public ResponseEntity<Resource> downloadFile01(HttpServletRequest request) {
try {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
// 檢查資源是否存在
if (resource.exists() || resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
log.error("文件模板下載失?。?, e);
return ResponseEntity.internalServerError().build();
}
}
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/2")
public ResponseEntity<StreamingResponseBody> downloadFile02(HttpServletRequest request) {
try {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
StreamingResponseBody body = outputStream -> {
FileUtil.writeToStream(resource.getFile(), outputStream);
};
// 檢查資源是否存在
if (resource.exists() || resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(body);
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
log.error("文件模板下載失??!", e);
return ResponseEntity.internalServerError().build();
}
}
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/3")
public void downloadFile03(HttpServletRequest request, HttpServletResponse response) throws IOException {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
// 設(shè)置響應(yīng)頭
response.setContentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());
response.setHeader("Content-Disposition", "attachment; filename=" + encodeFileName(request, resource.getFilename()));
response.setContentLengthLong(resource.contentLength());
FileUtil.writeToStream(resource.getFile(), response.getOutputStream());
}
@ApiOperation(value = "下載數(shù)據(jù)文檔模板")
@GetMapping("/download-template/4")
public ResponseEntity<byte[]> downloadFile04(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
Resource resource = new ClassPathResource("templates/ubuntu-20.04.6-desktop-amd64.iso");
// 檢查資源是否存在
if (resource.exists() || resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName(request, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(FileUtil.readBytes(resource.getFile()));
} else {
return ResponseEntity.notFound().build();
}
} catch (Exception e) {
log.error("文件模板下載失?。?, e);
return ResponseEntity.internalServerError().build();
}
}
/**
* 根據(jù)瀏覽器類型編碼文件名
* @param request 請求對象
* @param fileName 原始文件名
* @return 編碼后的文件名
*/
private static String encodeFileName(HttpServletRequest request, String fileName) {
String userAgent = request.getHeader("User-Agent");
// 處理IE及Edge瀏覽器
if (userAgent.contains("MSIE") || userAgent.contains("Trident") || userAgent.contains("Edge")) {
return cn.hutool.core.net.URLEncoder.createDefault().encode(fileName, StandardCharsets.UTF_8);
}
// 其他瀏覽器使用ISO-8859-1編碼
return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
}
}到此這篇關(guān)于spring文件下載的四種方式及對比分析的文章就介紹到這了,更多相關(guān)spring文件下載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot+Redis實現(xiàn)數(shù)據(jù)字典的方法
這篇文章主要介紹了SpringBoot+Redis實現(xiàn)數(shù)據(jù)字典的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
springboot bean循環(huán)依賴實現(xiàn)以及源碼分析
最近在使用Springboot做項目的時候,遇到了一個循環(huán)依賴的 問題,所以下面這篇文章主要給大家介紹了關(guān)于springboot bean循環(huán)依賴實現(xiàn)以及源碼分析的相關(guān)資料,需要的朋友可以參考下2021-06-06
基于SpringCloudGateway實現(xiàn)微服務(wù)網(wǎng)關(guān)的方式
Spring?Cloud?Gateway是Spring?官方基于Spring?5.0,Spring?Boot?2.0和Project?Reactor?等技術(shù)開發(fā)的網(wǎng)關(guān),旨在為微服務(wù)架構(gòu)提供一種簡單而有效的統(tǒng)一的API路由管理方式,對SpringCloudGateway實現(xiàn)微服務(wù)網(wǎng)關(guān)相關(guān)知識感興趣的朋友一起看看吧2021-12-12

