SpringBoot返回文件使前端下載的幾種方式小結(jié)
01 背景
在后端開發(fā)中,通常會有文件下載的需求,常用的解決方案有兩種:
- 不通過后端應(yīng)用,直接使用
nginx
直接轉(zhuǎn)發(fā)文件地址下載(適用于一些公開的文件,因為這里不需要授權(quán)) - 通過后端進行下載,同時進行一些業(yè)務(wù)處理
本篇主要以方法2
進行介紹,方法2
的原理步驟如下:
- 讀取文件,得到文件的字節(jié)流
- 將字節(jié)流寫入到響應(yīng)輸出流中
02 一次性讀取到內(nèi)存,通過響應(yīng)輸出流輸出到前端
@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()))) { // 一次性讀取到內(nèi)存中 byte[] buffer = new byte[is.available()]; int read = is.read(buffer); // 清空 response response.reset(); response.setCharacterEncoding("UTF-8"); // Content-Disposition的作用:告知瀏覽器以何種方式顯示響應(yīng)返回的文件,用瀏覽器打開還是以附件的形式下載到本地保存 // attachment表示以附件方式下載 inline表示在線打開 "Content-Disposition: inline; filename=文件名.mp3" // filename表示文件的默認名稱,因為網(wǎng)絡(luò)傳輸只支持URL編碼的相關(guān)支付,因此需要將文件名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); } }
適用于小文件,如果文件過大,一次性讀取到內(nèi)存中可能會出現(xiàn)oom的問題
03 將文件流通過循環(huán)寫入到響應(yīng)輸出流中(推薦)
@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)絡(luò)上獲取文件并返回給前端
@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)絡(luò)上獲取文本并下載到本地
@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 總結(jié)
一定要搞清楚 InputStream
和OutputStream
的區(qū)別,如果搞不清楚的,可以和字符流進行映射,InputStream -> Reader
,OutPutStream -> Writer
,換成這樣你就知道讀取內(nèi)容需要使用Reader
,寫入需要使用Writer
了。
返回給前端的是輸出流,不需要你顯示的去返回(return response;
),這樣會報錯
到此這篇關(guān)于SpringBoot返回文件使前端下載的幾種方式小結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot返回文件下載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實現(xiàn)在普通類中注入service或mapper
這篇文章主要介紹了java實現(xiàn)在普通類中注入service或mapper的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Spring裝配Bean之用Java代碼安裝配置bean詳解
這篇文章主要給大家介紹了關(guān)于Spring裝配Bean之用Java代碼安裝配置bean的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用spring具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧。2017-10-10基于Log4j2阻塞業(yè)務(wù)線程引發(fā)的思考
這篇文章主要介紹了基于Log4j2阻塞業(yè)務(wù)線程引發(fā)的思考,基于很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12RestTemplate對HttpClient的適配源碼解讀
這篇文章主要為大家介紹了RestTemplate對HttpClient的適配源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10