一、使用方法介紹
在Web應用程序中,我們常常需要提供文件的下載功能。Spring提供了一個ResponseEntity類,可以方便地下載文件。ResponseEntity類表示整個HTTP響應(即響應頭和響應體),可以設置響應的狀態碼、響應頭信息和響應體內容。通過設置響應頭信息,可以指定下載文件的文件名、文件大小和文件類型等信息。
示例:下載csv文件
@GetMapping("/downloadCsv") public ResponseEntity downloadCsv() throws IOException { String filename = "demo.csv"; String path = "file/demo.csv"; File file = new File(path); byte[] bytes = FileCopyUtils.copyToByteArray(file); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE); headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length())); return new ResponseEntity(bytes, headers, HttpStatus.OK); }
代碼中,先獲取要下載的文件,將文件以字節流的形式讀取到byte數組中,然後設置響應頭信息,並將byte數組、響應頭和響應狀態碼封裝到ResponseEntity對象中返回。
二、下載文件的注意事項
1、在設置下載文件名時,需要進行URL編碼。如果直接設置中文文件名,會導致下載的文件名亂碼。
示例:
String filename = "中文文件名.csv"; String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodedFilename);
2、在讀取文件時,要注意設置字節流的緩存大小,避免大文件導致內存溢出。
示例:
FileInputStream fileInputStream = new FileInputStream(file); byte[] buffer = new byte[1024]; int len; while ((len = fileInputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } fileInputStream.close();
3、如果下載的文件較大,建議使用分塊下載功能,分塊下載可以有效地減小服務器和客戶端的負載,並提高下載速度。
三、下載速度的優化
下載文件的速度影響了用戶的體驗,如何優化下載速度是我們需要考慮的一個問題。以下是一些優化方式:
1、啟用Gzip壓縮:可以啟用Gzip壓縮,可以減小傳輸文件的大小,從而提高下載速度。
示例:
@GetMapping("/downloadCsv") public ResponseEntity downloadCsv() throws IOException { // 省略獲取文件的代碼 byte[] bytes = FileCopyUtils.copyToByteArray(file); byte[] gzippedBytes = gzipCompress(bytes); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE); headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(gzippedBytes.length)); headers.add(HttpHeaders.CONTENT_ENCODING, "gzip"); return new ResponseEntity(gzippedBytes, headers, HttpStatus.OK); } private byte[] gzipCompress(byte[] bytes) throws IOException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream); gzipOutputStream.write(bytes); gzipOutputStream.close(); return byteArrayOutputStream.toByteArray(); }
2、啟用CDN加速:可以使用CDN加速服務,將文件存儲到CDN服務器上,利用CDN服務器的高速網絡,加速文件的下載。
3、使用斷點續傳:斷點續傳可以將文件分成多個部分,分別進行下載,如果下載中發生網絡異常,可以從已下載的部分繼續下載,避免重複下載已下載的部分,從而提高下載速度。
示例:
@GetMapping("/downloadBigFile") public void downloadBigFile(HttpServletRequest request, HttpServletResponse response) throws Exception { String filePath = "file/demo.mp4"; File file = new File(filePath); String fileName = file.getName(); long fileLength = file.length(); long startPos = 0; long endPos = fileLength - 1; long contentLength = endPos - startPos + 1; response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength)); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName); response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); byte[] buffer = new byte[1024 * 1024]; RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); if(request.getHeader(HttpHeaders.RANGE) == null) { response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileLength)); randomAccessFile.seek(startPos); OutputStream outputStream = response.getOutputStream(); while (contentLength > 0) { int read = randomAccessFile.read(buffer, 0, (int) Math.min(buffer.length, contentLength)); contentLength -= read; outputStream.write(buffer, 0, read); outputStream.flush(); } randomAccessFile.close(); outputStream.close(); return; } response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); String range = request.getHeader(HttpHeaders.RANGE); range = range.replaceAll("bytes=", "").trim(); String[] ranges = range.split("-"); if(!StringUtils.isEmpty(ranges[0])) { startPos = Long.parseLong(ranges[0]); } if(ranges.length > 1 && !StringUtils.isEmpty(ranges[1])) { endPos = Long.parseLong(ranges[1]); } contentLength = endPos - startPos + 1; response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength)); response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + startPos + "-" + endPos + "/" + fileLength); randomAccessFile.seek(startPos); OutputStream outputStream = response.getOutputStream(); while (contentLength > 0) { int read = randomAccessFile.read(buffer, 0, (int) Math.min(buffer.length, contentLength)); contentLength -= read; outputStream.write(buffer, 0, read); outputStream.flush(); } randomAccessFile.close(); outputStream.close(); }
代碼中,首先判斷是否支持斷點續傳,如果支持,則解析HTTP請求頭中的Range字段,計算出下載的起始位置和結束位置,設置響應頭,將文件的指定部分發送給客戶端。如果不支持斷點續傳,則直接將整個文件發送給客戶端。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/193384.html