下載文件的ResponseEntity使用方法詳解

一、使用方法介紹

在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-tw/n/193384.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-01 15:01
下一篇 2024-12-01 15:01

相關推薦

發表回復

登錄後才能評論