SpringBoot返回文件使前端下載的幾種方式小結
01 背景
在后端開發(fā)中,通常會有文件下載的需求,常用的解決方案有兩種:
- 不通過后端應用,直接使用
nginx直接轉發(fā)文件地址下載(適用于一些公開的文件,因為這里不需要授權) - 通過后端進行下載,同時進行一些業(yè)務處理
本篇主要以方法2進行介紹,方法2的原理步驟如下:
- 讀取文件,得到文件的字節(jié)流
- 將字節(jié)流寫入到響應輸出流中
02 一次性讀取到內存,通過響應輸出流輸出到前端
@GetMapping("/file/download")
public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {
File file = new File(filePath);
if (!file.exists()) {
throw new BusinessException("當前下載的文件不存在,請檢查路徑是否正確");
}
// 將文件寫入輸入流
try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
// 一次性讀取到內存中
byte[] buffer = new byte[is.available()];
int read = is.read(buffer);
// 清空 response
response.reset();
response.setCharacterEncoding("UTF-8");
// Content-Disposition的作用:告知瀏覽器以何種方式顯示響應返回的文件,用瀏覽器打開還是以附件的形式下載到本地保存
// attachment表示以附件方式下載 inline表示在線打開 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默認名稱,因為網(wǎng)絡傳輸只支持URL編碼的相關支付,因此需要將文件名URL編碼后進行傳輸,前端收到后需要反編碼才能獲取到真正的名稱
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
// 告知瀏覽器文件的大小
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
適用于小文件,如果文件過大,一次性讀取到內存中可能會出現(xiàn)oom的問題
03 將文件流通過循環(huán)寫入到響應輸出流中(推薦)
@GetMapping("/file/download")
public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {
File file = new File(filePath);
if (!file.exists()) {
throw new BusinessException("當前下載的文件不存在,請檢查路徑是否正確");
}
// 清空 response
response.reset();
response.setCharacterEncoding("UTF-8");
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
response.setContentType("application/octet-stream");
// 將文件讀到輸入流中
try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
byte[] buffer = new byte[1024];
int len;
//從輸入流中讀取一定數(shù)量的字節(jié),并將其存儲在緩沖區(qū)字節(jié)數(shù)組中,讀到末尾返回-1
while((len = is.read(buffer)) > 0){
outputStream.write(buffer, 0, len);
}
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
04 從網(wǎng)絡上獲取文件并返回給前端
@GetMapping("/net/download")
public void netDownload(HttpServletResponse response, @RequestParam("fileAddress") String fileAddress, @RequestParam("filename") String filename) {
try {
URL url = new URL(fileAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
response.reset();
response.setContentType(conn.getContentType());
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
byte[] buffer = new byte[1024];
int len;
OutputStream outputStream = response.getOutputStream();
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
05 從網(wǎng)絡上獲取文本并下載到本地
@GetMapping("/netDownloadLocal")
public void downloadNet(@RequestParam("netAddress") String netAddress, @RequestParam("filepath") String filepath) {
try {
URL url = new URL(netAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(filepath);
int byteread;
byte[] buffer = new byte[1024];
while ((byteread = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, byteread);
}
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
06 總結
一定要搞清楚 InputStream和OutputStream的區(qū)別,如果搞不清楚的,可以和字符流進行映射,InputStream -> Reader,OutPutStream -> Writer,換成這樣你就知道讀取內容需要使用Reader,寫入需要使用Writer了。
返回給前端的是輸出流,不需要你顯示的去返回(return response;),這樣會報錯
到此這篇關于SpringBoot返回文件使前端下載的幾種方式小結的文章就介紹到這了,更多相關SpringBoot返回文件下載內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
如何使用mybatis-plus實現(xiàn)分頁查詢功能
最近在研究mybatis,然后就去找簡化mybatis開發(fā)的工具,發(fā)現(xiàn)就有通用Mapper和mybatis-plus兩個比較好的可是使用,可是經過對比發(fā)現(xiàn)還是mybatis-plus比較好,下面這篇文章主要給大家介紹了關于如何使用mybatis-plus實現(xiàn)分頁查詢功能的相關資料,需要的朋友可以參考下2022-06-06
使用SpringBoot動態(tài)切換數(shù)據(jù)源的實現(xiàn)方式
在我們企業(yè)項目開發(fā)的過程中,有的時候,一個項目需要在運行時,根據(jù)某種條件選擇使用哪個數(shù)據(jù)源,那么此時該怎么進行動態(tài)切換呢,本文給大家例舉一種常見的實現(xiàn)方式,文中有詳細的實現(xiàn)步驟,需要的朋友可以參考下2023-12-12
Spring Boot統(tǒng)一異常攔截實踐指南(最新推薦)
本文介紹了Spring Boot中統(tǒng)一異常處理的重要性及實現(xiàn)方案,包括使用`@ControllerAdvice`和`@ExceptionHandler`注解,實現(xiàn)全局異常處理和統(tǒng)一響應格式,本文給大家介紹的非常詳細,感興趣的朋友一起看看吧2025-02-02
java?實現(xiàn)獲取指定位置后的第一個數(shù)字
這篇文章主要介紹了java?實現(xiàn)獲取指定位置后的第一個數(shù)字,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
面試官:Java中new Object()到底占用幾個字節(jié)
這篇文章主要介紹了面試官:Java中new Object()到底占用幾個字節(jié),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02

