Spring 大文件下载(前端视频播放)优化
-
优化前代码
@GetMapping("/download") public void download(HttpServletResponse res, @RequestParam("fileName") String fileName) { GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(BUCKETNAME) .object(fileName).build(); try (GetObjectResponse response = minioClient.getObject(objectArgs)){ byte[] buf = new byte[1024]; int len; try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){ while ((len=response.read(buf))!=-1){ os.write(buf,0,len); } os.flush(); byte[] bytes = os.toByteArray(); res.setCharacterEncoding("utf-8"); res.setContentType("application/octet-stream"); res.setContentLength(bytes.length); res.addHeader("Content-Disposition", "attachment;filename=" + FileUtil.getName(fileName)); try (ServletOutputStream stream = res.getOutputStream()){ stream.write(bytes); stream.flush(); } } } catch (Exception e) { e.printStackTrace(); } }
-
优化后。我们解析请求
header
中Range
实现分片下载。这样就能实现拖拽播放,不用将文件一次性全部下载完毕。@GetMapping("/download") public void download(HttpServletResponse res, HttpServletRequest request, @RequestParam("fileName") String fileName) { GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(BUCKETNAME) .object(fileName).build(); StatObjectArgs statObjectArgs = StatObjectArgs.builder().bucket(BUCKETNAME).object(fileName).build(); long fileSize; try { fileSize = minioClient.statObject(statObjectArgs).size(); } catch (Exception e) { log.error("文件下载失败:{}", fileName, e); return; } String range = request.getHeader("Range"); if(range != null && range.startsWith("bytes=")) { try { int idx = range.indexOf("-"); long start = Long.parseLong(range.substring("bytes=".length(), idx)); long end = minioClient.statObject(statObjectArgs).size() - 1; if (idx + 1 < range.length()) { end = Long.parseLong(range.substring(idx + 1)); } long length = end - start + 1; InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(BUCKETNAME).object(fileName).offset(start).length(length).build()); res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); res.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize); res.setContentType("application/octet-stream"); res.setHeader("Content-Disposition", "attachment; filename=" + FileUtil.getName(fileName)); res.setContentLength((int)length); try { IOUtils.copy(inputStream, res.getOutputStream()); inputStream.close(); res.flushBuffer(); } catch (ClientAbortException e) { // pass } } catch (Exception e) { log.error("断点下载失败:{}", fileName, e); } } else { try (InputStream is = minioClient.getObject(objectArgs)) { res.setContentType("application/octet-stream"); res.addHeader("Content-Disposition", "attachment;filename=" + FileUtil.getName(fileName)); res.setContentLength((int)fileSize); IOUtils.copy(is, res.getOutputStream()); res.flushBuffer(); } catch (Exception e) { log.error("文件下载失败:{}", fileName, e); } } }