一、使用方法介绍
在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/n/193384.html
微信扫一扫
支付宝扫一扫