一、使用方法介绍
在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