spring文件下載的方式

spring文件下載的方式

    • 方式一:通過ResponseEntity<Resource> 方式來下載
    • 方式二:通過ResponseEntity<StreamingResponseBody> 方式來下載
    • 方式三:通過Servlet原生下載
    • 方式四:通過ResponseEntity<byte[]> 方式來下載
    • 四種下載方式的對比
      • 1、核心特性對比
      • 2、典型場景推薦
    • 完整的代碼

方式一:通過ResponseEntity 方式來下載

@ApiOperation(value = "下載數據文檔模板")
@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();}
}/*** 根據瀏覽器類型編碼文件名* @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 = "下載數據文檔模板")
@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 = "下載數據文檔模板")
@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");// 設置響應頭response.setContentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());response.setHeader("Content-Disposition", "attachment; filename=" + encodeFileName(request, resource.getFilename()));//	這里是關鍵,如果設置 response.setContentLength() 則下載的文件只能是 50 多兆response.setContentLengthLong(resource.contentLength());FileUtil.writeToStream(resource.getFile(), response.getOutputStream());
}

方式四:通過ResponseEntity<byte[]> 方式來下載

@ApiOperation(value = "下載數據文檔模板")
@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、核心特性對比

方式??內存占用??適用場景??流式支持??資源管理??代碼復雜度?
ResponseEntity<byte[]>高(全量加載)小文件(<10MB)如配置文件、圖片?自動釋放內存低(簡單封裝)
ResponseEntity<Resource>中(按需加載)動態資源(如JAR內文件、數據庫Blob)?(需手動關閉流)需顯式關閉InputStream中(需處理流)
ResponseEntity<StreamingResponseBody>極低(分塊傳輸)超大文件(視頻、日志等)?(自動分塊)自動處理流生命周期高(需分塊邏輯)
Servlet原生下載低(手動控制)需要精細控制響應頭/流的場景?(手動實現)需手動關閉流高(冗余代碼)

2、典型場景推薦

  • ?快速小文件下載?:優先使用ResponseEntity<byte[]>,直接返回字節數組,無需流控制。
  • ?動態資源或加密文件?:選擇ResponseEntity<Resource>,靈活處理輸入流。
  • ?**超大文件(GB級)**?:必須用StreamingResponseBody分塊傳輸,避免OOM。
  • ?兼容舊系統或特殊需求?:Servlet原生下載(如需要自定義響應頭邏輯)

完整的代碼

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 = "下載數據文檔模板")@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 = "下載數據文檔模板")@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 = "下載數據文檔模板")@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");// 設置響應頭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 = "下載數據文檔模板")@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();}}/*** 根據瀏覽器類型編碼文件名* @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);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95300.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95300.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95300.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

寫一個redis客戶端軟件,參考 Another Redis Desktop Manager 的設計風格。

一個基于 Electron 開發的現代化 Redis 桌面客戶端&#xff0c;參考 Another Redis Desktop Manager 的設計風格。 github倉庫地址 https://github.com/henkuoai/redis-man-pc

Web3: DeFi借貸的安全基石, 了解喂價與清算機制的原理與重要性

今天我們要聊一個DeFi世界里至關重要&#xff0c;但又時常被誤解的話題&#xff1a;為什么DeFi協議需要定期更新喂價和執行清算&#xff1f; 如果大家參與過DeFi借貸&#xff0c;大家可能看到過“清算”這個詞&#xff0c;甚至會有點談虎色變。但實際上&#xff0c;清算和為其提…

「iOS」————響應者鏈與事件傳遞鏈

iOS學習響應者鏈和事件傳遞鏈傳遞鏈&#xff1a;hitTest:withEvent**pointInside:withEvent**響應鏈第一響應者和最佳響應者觸摸事件&#xff08;UITouch&#xff09;UIGestureRecognizer&#xff08;手勢識別器&#xff09;響應者鏈和事件傳遞鏈 iOS事件的主要由&#xff1a;…

修復圖像、視頻和3D場景的AI工具–Inpaint Anything

TL; DR&#xff1a;用戶可以通過單擊來選擇圖像中的任何對象。借助強大的視覺模型&#xff0c;例如SAM、LaMa和穩定擴散 (SD)&#xff0c;Inpaint Anything能夠順利地移除對象&#xff08;即Remove Anything&#xff09;。此外&#xff0c;在用戶輸入文本的提示下&#xff0c;I…

java -jar xxx.jar 提示xxx.jar中沒有主清單屬性報錯解決方案

xxx.jar 中沒有主清單屬性 &#xff08;no main manifest attribute&#xff09;解決方案 java -jar xxx.jar 提示xxx.jar中沒有主清單屬性報錯解決方案 這個錯通常出現在你用 java -jar xxx.jar 啟動&#xff0c;但 JAR 的 META-INF/MANIFEST.MF 里沒有 Main-Class 條目&#…

Myqsl建立庫表練習

目錄 一、windows中選擇一種方式安裝Mysql8.0 二、新建產品庫mydb6_product 1. 新建3張表如下&#xff1a; 1&#xff09;employees表 2&#xff09;orders表 3&#xff09;invoices表 三、新建員工庫mydb8_worker&#xff0c;添加自定義表內容并插入數據 1. 新建庫表 2. 插…

STM32 輸入捕獲,串口打印,定時器,中斷綜合運用

實驗目的 使用定時器 2 通道 2 來捕獲按鍵 2 按下時間&#xff0c;并通過串口打印。 計一個數的時間&#xff1a;1us&#xff0c;PSC71&#xff0c;ARR65535 下降沿捕獲、輸入通道 2 映射在 TI2 上、不分頻、不濾波輸入捕獲原理定時器輸入捕獲實驗配置步驟測量按鍵按下時長思路…

Nacos-2--Nacos1.x版本的通信原理

在Nacos 1.x版本中&#xff0c;客戶端長輪詢&#xff08;Long Polling&#xff09;和服務端UDP主動推送是兩種不同的機制&#xff0c;分別用于配置管理和服務發現場景。它們的核心目標都是實現動態更新的實時感知&#xff0c;但實現方式、數據內容和適用場景完全不同。 1、長輪…

機器學習——09 聚類算法

1 聚類算法聚類算法&#xff1a; 是一種無監督學習算法&#xff0c;它不需要預先知道數據的類別信息&#xff0c;而是根據樣本之間的相似性&#xff0c;將樣本劃分到不同的類別中&#xff1b;不同的相似度計算方法&#xff0c;會得到不同的聚類結果&#xff0c;常用的相似度計算…

生成式AI應用生態的爆發與專業化演進:從零和博弈到正和共贏

2025年,生成式AI產業規模已突破7000億元,全球生成式AI市場規模預計在2028年達到2842億美元(IDC數據)。在這場技術革命中,AI基礎模型的分化已證明:差異化競爭而非同質化替代,才是推動產業發展的核心邏輯。如今,這一規律正從基礎模型層向應用生成平臺層蔓延——Lovable、…

Mysql——Sql的執行過程

目錄 一、Sql的執行過程流程圖解 二、Sql的執行過程流程 1.2.1、建立連接 1.2.2、服務層(緩存、解析器、預處理器、優化器、執行器) 1.2.2.1、緩存 1.2.2.2、解析器 1.2.2.3、預處理器 1.2.2.4、優化器 1.2.2.5、執行器 1.2.3、引擎層 一、Sql的執行過程流程圖解 Sql的執行過…

【Axure高保真原型】地圖路線和定位

今天和大家分享地圖路線和定位的原型模版&#xff0c;載入后&#xff0c;可以查看汽車行進路線和所在定位 提供了停靠和不停靠站點兩個案例&#xff0c;具體效果可以打開下方原型地址體驗或者點擊下方視頻觀看 【Axure高保真原型】地圖路線和定位【原型預覽含下載地址】 https…

【96頁PPT】華為IPD流程管理詳細版(附下載方式)

篇幅所限&#xff0c;本文只提供部分資料內容&#xff0c;完整資料請看下面鏈接 https://download.csdn.net/download/2501_92808811/91633108 資料解讀&#xff1a;華為IPD流程管理詳細版 詳細資料請看本解讀文章的最后內容 華為的集成產品開發&#xff08;IPD&#xff09;…

深度解析Mysql的開窗函數(易懂版)

SQL 開窗函數&#xff08;Window Function&#xff09;是一種強大的分析工具&#xff0c;它能在保留原有數據行的基礎上&#xff0c;對 "窗口"&#xff08;指定范圍的行集合&#xff09;進行聚合、排名或分析計算&#xff0c;解決了傳統GROUP BY聚合會合并行的局限性…

Java靜態代理和動態代理

Java靜態代理和動態代理 靜態代理 現在有一個計算類&#xff0c;有四個方法&#xff0c;加減乘除&#xff0c;如果需要給這四個方法都加上同一個邏輯&#xff0c;可以創建一個類作為代理類&#xff0c;把計算類注入到這個類中&#xff0c;然后再代理類中定義方法&#xff0c;并…

MySQL——MySQL引擎層BufferPool工作過程原理

目錄一、MySQL引擎層BufferPool工作過程圖解二、MySQL引擎層BufferPool工作過程原理一、MySQL引擎層BufferPool工作過程圖解 圖解 二、MySQL引擎層BufferPool工作過程原理 首先關閉自動提交&#xff0c;執行一條修改語句。 SET AUTOCOMMIT 0; update employees set name張三…

Python初學者筆記第二十二期 -- (JSON數據解析)

第31節課 JSON數據解析 1.JSON基礎概念 JSON 是一種輕量級的數據交換格式&#xff08;另一個叫XML&#xff09;&#xff0c;具有簡潔、易讀的特點&#xff0c;并且在不同編程語言之間能很好地實現數據傳遞。在 Python 中&#xff0c;json模塊能夠實現 Python 數據類型與 JSON 數…

基于多模態大模型的個性化學習路徑生成系統研究

摘要 隨著互聯網技術的迅猛發展&#xff0c;個性化學習路徑生成系統的研究在教育領域日益凸顯其重要性。本研究聚焦于基于多模態大模型的個性化學習路徑生成系統&#xff0c;旨在通過整合多模態數據&#xff0c;為學習者提供更加精準、個性化的學習路徑。多模態大模型&#xf…

ESP32 燒錄固件失敗原因排除

ESP32 燒錄固件時&#xff0c;有哪些特殊引腳需要注意電平狀態的在 ESP32 燒錄固件時&#xff0c;有幾個關鍵引腳的電平狀態會直接影響燒錄過程&#xff0c;需要特別注意&#xff1a;GPIO0&#xff08;BOOT 引腳&#xff09;&#xff1a;燒錄模式&#xff1a;需要拉低&#xff…

3D視覺系統在機器人行業中的應用

視覺引導機器人技術&#xff08;VGR&#xff09;具有成熟的2D成像技術&#xff0c;但是經濟高效的3D技術的出現使機器人應用的可能性更大。工業自動化的第一次迭代使用“盲”機器人&#xff0c;該機器人取決于待處理材料的精確定位。這樣的機器人相對不靈活&#xff0c;只能通過…