spring boot大文件與多文件下載

一、簡單大文件下載:

/*** 下載大文件* @param path 路徑* @param fileName 文件名* @return* @throws IOException*/
public static ResponseEntity<InputStreamResource> downloadFile(String path, String fileName) throws IOException {Path filePath = Paths.get(path);long size = Files.size(filePath);InputStreamResource resource = new InputStreamResource(Files.newInputStream(filePath));HttpHeaders headers = new HttpHeaders();headers.add(HttpHeaders.CONTENT_DISPOSITION, STR."attachment; filename=\{URLEncoder.encode(fileName, StandardCharsets.UTF_8)}");headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);headers.add(HttpHeaders.ACCEPT_RANGES, "bytes");return ResponseEntity.ok().headers(headers).contentLength(size).body(resource);
}

二、多文件下載:

/*** 起線程壓縮文件,返回流* @param pathList 路徑集合* @param fileName 文件名* @return*/
public static ResponseEntity<InputStreamResource> zipByThread(List<String> pathList, String fileName) throws IOException {PipedInputStream pipedInputStream = new PipedInputStream();PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream);// 啟動新線程以寫入管道輸出流Thread.ofVirtual().start(() -> {try (ZipOutputStream zos = new ZipOutputStream(pipedOutputStream)) {for (String path : pathList) {Path filePath = Paths.get(path);if (Files.exists(filePath)) {if (Files.isDirectory(filePath)) {zipDirectory(filePath, filePath.getFileName().toString(), zos);} else {zipFile(filePath, zos);}}}} catch (IOException e) {log.error("文件壓縮失敗:{}", e.getMessage());throw new BusinessException("文件壓縮失敗");} finally {try {pipedOutputStream.close();} catch (IOException _) {}}});HttpHeaders headers = new HttpHeaders();headers.add(HttpHeaders.CONTENT_DISPOSITION, STR."attachment; filename=\{URLEncoder.encode(fileName, StandardCharsets.UTF_8)}");headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(-1));return ResponseEntity.ok().headers(headers).body(new InputStreamResource(pipedInputStream));
}/*** 壓縮文件夾* @param folder* @param parentFolder* @param zos* @throws IOException*/
private static void zipDirectory(Path folder, String parentFolder, ZipOutputStream zos) throws IOException {try (Stream<Path> paths = Files.walk(folder)) {paths.filter(Files::isRegularFile).forEach(path -> {String zipEntryName = Paths.get(parentFolder).resolve(folder.relativize(path)).toString().replace("\\", "/");try {zos.putNextEntry(new ZipEntry(zipEntryName));Files.copy(path, zos);zos.closeEntry();} catch (IOException e) {throw new BusinessException("壓縮文件夾失敗");}});}
}/*** 壓縮文件* @param file* @param zos* @throws IOException*/
private static void zipFile(Path file, ZipOutputStream zos) throws IOException {zos.putNextEntry(new ZipEntry(file.getFileName().toString()));Files.copy(file, zos);zos.closeEntry();
}

三、 Reactive WebFlux 實現非阻塞流式傳輸(推薦)

1.引入依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.單文件下載
/*** 響應式流式下載文件,支持斷點續傳* @param folderPath 文件路徑* @param fileName 文件名* @return 響應實體*/
@GetMapping("/download/one")
public Mono<ResponseEntity<Resource>> download(@RequestParam String folderPath,@RequestParam String fileName) throws IOException {// 獲取文件Path basePath = Paths.get(folderPath).toAbsolutePath().normalize();Path filePath = basePath.resolve(fileName).normalize();// 校驗文件路徑,防止訪問到其他目錄if (!filePath.startsWith(basePath)) {throw new SecurityException("文件路徑不在允許的目錄內");}// 讀取文件Resource resource = new PathResource(filePath);if (!resource.exists()) {return Mono.just(ResponseEntity.notFound().build());}// 獲取文件大小long contentLength = resource.contentLength();// 獲取文件類型MediaType mediaType = MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);// 響應文件return Mono.just(ResponseEntity.ok().contentType(mediaType).contentLength(contentLength).header(HttpHeaders.ACCEPT_RANGES, "bytes").body(resource));
}
3.多文件下載
/*** 響應式流式下載多個文件,不支持斷點續傳,因為需要壓縮多個文件,長度未知* @param folderPath 文件路徑* @param fileNames 文件名列表* @return 響應實體*/
@GetMapping("/download/multiple")
public Mono<ResponseEntity<InputStreamResource>> downloadMultipleFile(@RequestParam String folderPath,@RequestParam List<String> fileNames) throws IOException {// 驗證文件路徑Path basePath = Paths.get(folderPath).toAbsolutePath().normalize();List<Path> filePaths = new ArrayList<>(fileNames.size());for (String fileName : fileNames) {Path filePath = basePath.resolve(fileName).normalize();if (!filePath.startsWith(basePath)) {throw new SecurityException("文件路徑不在允許的目錄內");}filePaths.add(filePath);}// 創建管道流PipedOutputStream pos = new PipedOutputStream();PipedInputStream pis = new PipedInputStream(pos, 1024 * 32);// 在單獨的線程中執行壓縮Thread.ofVirtual().start(() -> {try (ZipOutputStream zos = new ZipOutputStream(pos)) {// 設置壓縮級別zos.setLevel(Deflater.BEST_SPEED);for (Path path : filePaths) {if (!Files.exists(path)) {continue;}if (Files.isDirectory(path)) {zipDirectory(path, path.getFileName().toString(), zos);} else {zipFile(path, zos);}}zos.finish();} catch (IOException e) {// 處理錯誤} finally {try {pos.close();} catch (IOException e) {// 忽略關閉異常}}});HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);headers.setContentDispositionFormData("attachment", "download.zip");headers.setContentLength(-1);return Mono.just(ResponseEntity.ok().headers(headers).body(new InputStreamResource(pis)));
}/*** 壓縮文件夾* @param folder 文件夾路徑* @param parentFolder 父文件夾名* @param zos 壓縮輸出流* @throws IOException 異常*/
private static void zipDirectory(Path folder, String parentFolder, ZipOutputStream zos) throws IOException {try (Stream<Path> paths = Files.walk(folder)) {paths.filter(Files::isRegularFile).forEach(path -> {String zipEntryName = Paths.get(parentFolder).resolve(folder.relativize(path)).toString().replace("\\", "/");try {zos.putNextEntry(new ZipEntry(zipEntryName));Files.copy(path, zos);zos.closeEntry();} catch (IOException e) {throw new RuntimeException("壓縮文件夾失敗");}});}
}/*** 壓縮文件* @param file 文件路徑* @param zos 壓縮輸出流* @throws IOException 異常*/
private static void zipFile(Path file, ZipOutputStream zos) throws IOException {zos.putNextEntry(new ZipEntry(file.getFileName().toString()));Files.copy(file, zos);zos.closeEntry();
}

注意

如果使用了nginx反代,下載超時需要在nginx配置

proxy_buffering off;

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

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

相關文章

第二節:React 基礎篇-受控組件 vs 非受控組件

一、場景題&#xff1a;設計一個實時搜索輸入框&#xff0c;說明選擇依據 受控組件 vs 非受控組件 核心區別 特征受控組件非受控組件數據管理由React狀態&#xff08;state&#xff09;控制通過DOM元素&#xff08;ref&#xff09;直接訪問更新時機每次輸入觸發onChange提交…

局部路由守衛

局部路由守衛為我們提供了更細粒度的路由控制&#xff0c;允許我們在特定的路由或組件級別添加鑒權和邏輯處理。局部路由守衛分為 path 守衛和 component 守衛&#xff0c;它們分別適用于不同的場景。 path 守衛&#xff08;路由守衛&#xff09; path 守衛用于在進入特定路由…

Android 16應用適配指南

Android 16版本特性介紹 https://developer.android.com/about/versions/16?hlzh-cn Android 16 所有功能和 API 概覽 https://developer.android.com/about/versions/16/features?hlzh-cn#language-switching Android 16 發布時間 Android 16 適配指南 Google開發平臺&…

android display 筆記(十二)CPU,GPU,DPU的區別

CPU&#xff08;Central Processing Unit&#xff09;通用計算&#xff1a;處理復雜邏輯、分支預測、多任務調度。 低延遲&#xff1a;優先快速響應單線程任務。 GPU&#xff08;Graphics Processing Unit&#xff09; 高吞吐量并行計算&#xff1a;適合大規模數據并行處理。…

音頻轉文本:如何識別音頻成文字

Python腳本:MP4轉MP3并語音識別為中文 以下是一個完整的Python腳本,可以將MP4視頻轉換為MP3音頻,然后使用語音識別模型將音頻轉換為中文文本。 準備工作 首先需要安裝必要的庫: pip install moviepy pydub SpeechRecognition openai-whisper完整腳本 import os from m…

理解 MCP 協議的數據傳遞:HTTP 之上的一層“殼子

以下是以 CSDN 博客的風格記錄你對 MCP 協議數據傳遞的理解和發現&#xff0c;內容涵蓋了 MCP 協議基于 HTTP 的本質、JSON-RPC 的“殼子”作用&#xff0c;以及為什么熟悉 HTTP 協議就足以理解 MCP 的數據傳遞。文章面向技術社區&#xff0c;結構清晰&#xff0c;適合分享。 理…

基于ssm網絡游戲推薦系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 當今社會進入了科技進步、經濟社會快速發展的新時代。國際信息和學術交流也不斷加強&#xff0c;計算機技術對經濟社會發展和人民生活改善的影響也日益突出&#xff0c;人類的生存和思考方式也產生了變化。傳統網絡游戲管理采取了人工的管理方法&#xff0c;但這種管理方…

vue入門:指令

文章目錄 vue的內置指令說明&#xff1a; 自定義指令 vue的內置指令 Vue 指令的本質是&#xff1a; 聲明式的 DOM 操作接口&#xff08;隱藏底層 JavaScript 代碼&#xff09;。響應式數據的綁定媒介&#xff08;連接數據和視圖&#xff09;。模板編譯的標記&#xff08;最終…

oracle 索引失效

在 Oracle 11g 中&#xff0c;索引失效的常見原因包括函數修改列、隱式類型轉換、統計信息過時等&#xff0c;解決方法需結合版本特性&#xff08;如虛擬列、索引跳躍掃描&#xff09;。通過執行計劃分析、統計信息維護和合理使用提示&#xff08;Hints&#xff09;&#xff0c…

k8s藍綠發布

k8s藍綠發布 什么是藍綠部署K8S中如何實現藍綠部署k8s藍綠部署流程圖 什么是藍綠部署 參考: https://youtu.be/CLq_hA0lAd0 https://help.coding.net/docs/cd/best-practice/blue-green.html 藍綠部署最早是由馬丁福勒 2010年在他的博客中提出. 藍綠部署是一種軟件部署策略,用…

stm32面試

數據結構相關問題 stm32面試 數據結構相關問題 目錄基礎數據結構樹與圖排序與查找算法 Linux相關問題Linux系統基礎Linux命令與腳本Linux網絡與服務 操作系統相關問題操作系統基礎概念操作系統調度算法操作系統同步與通信 STM32相關問題STM32硬件基礎STM32編程與開發STM32應用與…

Mybatis 中 mappers標簽 package的使用

MyBatis 的配置文件中&#xff0c;<mappers> 標簽用于指定 MyBatis 應該加載哪些映射器&#xff08;Mapper&#xff09;。其中 package 屬性是一種便捷的方式來批量注冊多個映射器接口 package 屬性允許你指定一個包名&#xff0c;MyBatis 會自動掃描該包下的所有映射器…

設計模式 --- 訪問者模式

訪問者模式是一種行為設計模式&#xff0c;它允許在不改變對象結構的前提下&#xff0c;定義作用于這些對象元素的新操作。 優點&#xff1a; 1.??符合開閉原則&#xff1a;新增操作只需添加新的訪問者類&#xff0c;無需修改現有對象結構。 ??2.操作邏輯集中管理??&am…

監控docker中的java應用

1)進入指定的容器 docker exec -it demo /bin/bash 2)下載curl root89a67e345354:/# apt install curl -y 3)下載arthas root89a67e345354:/# curl -O https://arthas.aliyun.com/arthas-boot.jar 4)運行 root89a67e345354:/# java -jar arthas-boot.jar 5)監控 […

最新版RubyMine超詳細圖文安裝教程,帶補丁包(2025最新版保姆級教程)

目錄 前言 一、RubyMine最新版下載 二、RubyMine安裝 三、RubyMine補丁 四、運行RubyMine 前言 RubyMine是由JetBrains開發的集成開發環境&#xff08;IDE&#xff09;&#xff0c;專為Ruby和Ruby on Rails開發者設計&#xff0c;提供智能代碼補全、調試、測試、版本控制集…

數字政府與智慧城市區別報告分析

數字政府與智慧城市區別報告分析 一、引言 隨著信息技術的飛速發展&#xff0c;數字政府和智慧城市成為推動城市現代化和治理能力提升的兩個重要概念。雖然它們都依托于信息技術&#xff0c;但二者在目標、內涵、實施路徑等方面存在顯著差異。本文旨在深入探討數字政府與智慧…

[MRCTF2020]ezpop wp

本題考點:php反序列化的pop鏈 首先來了解一下pop鏈是什么,它類似于多米諾骨牌一環套一環,要調用這個成員方法然后去找能調用這個方法的魔術方法,最后一環接一環,完成一個鏈子,最終形成payload。 那么來了解一下這些魔術方法 __construct() //類的構造函數&#xff0…

spark架構和RDD相關概念

運行架構&#xff1a; Spark采用master - slave結構&#xff0c;Driver作為master負責作業任務調度&#xff0c;Executor作為slave負責實際執行任務。 核心組件 Driver&#xff1a;執行Spark任務的main方法&#xff0c;將用戶程序轉化為作業&#xff0c;在Executor間調度任務&…

安全是基石

“安全是基石”這句話強調了安全在個人、企業、社會等各個層面中的基礎性和不可替代的重要性。無論是物理安全、網絡安全、數據安全&#xff0c;還是生產安全、公共安全&#xff0c;都是保障穩定發展的前提。以下是不同領域中“安全”作為基石的體現&#xff1a; 1. 個人安全 基…

Linux Makefile-概述、語句格式、編寫規則、多文件編程、Makefile變量分類:自定義變量、預定義變量

目錄 1.make 1.1 make 命令格式 2.Makefile 核心概念? ? 2.1創建并運行 Makefile步驟 3. Makefile編寫 3.1最基礎Makefile 3.1.1使用默認make命令 3.1.2使用make -f 命令 3.1.2.1 使用make -f 命令執行默認make操作 3.1.2.2使用 make [ ‐f file ] [ targets ]命令 …