Commit 2ad1001e94c75aa423da7095546b18af68c3e837
1 parent
8679ad21
修复录像并发下载内存占用过多的问题
Showing
2 changed files
with
198 additions
and
174 deletions
src/main/java/top/panll/assist/config/WebMvcConfig.java
0 → 100644
| 1 | +package top.panll.assist.config; | ||
| 2 | + | ||
| 3 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 4 | +import org.springframework.context.annotation.Configuration; | ||
| 5 | +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | ||
| 6 | +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; | ||
| 7 | +import top.panll.assist.dto.UserSettings; | ||
| 8 | + | ||
| 9 | +import java.io.File; | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +@Configuration | ||
| 13 | +public class WebMvcConfig extends WebMvcConfigurerAdapter { | ||
| 14 | + | ||
| 15 | + @Autowired | ||
| 16 | + private UserSettings userSettings; | ||
| 17 | + | ||
| 18 | + @Override | ||
| 19 | + public void addResourceHandlers(ResourceHandlerRegistry registry) { | ||
| 20 | + File file = new File(userSettings.getRecord()); | ||
| 21 | + registry.addResourceHandler("/download/**").addResourceLocations("file://" + file.getAbsolutePath() + "/"); | ||
| 22 | + super.addResourceHandlers(registry); | ||
| 23 | + } | ||
| 24 | +} |
src/main/java/top/panll/assist/controller/DownloadController.java
| 1 | -package top.panll.assist.controller; | ||
| 2 | - | ||
| 3 | - | ||
| 4 | -import io.swagger.v3.oas.annotations.Operation; | ||
| 5 | -import io.swagger.v3.oas.annotations.Parameter; | ||
| 6 | -import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 7 | -import org.apache.catalina.connector.ClientAbortException; | ||
| 8 | -import org.slf4j.Logger; | ||
| 9 | -import org.slf4j.LoggerFactory; | ||
| 10 | -import org.springframework.beans.factory.annotation.Autowired; | ||
| 11 | -import org.springframework.stereotype.Controller; | ||
| 12 | -import org.springframework.web.bind.annotation.*; | ||
| 13 | -import top.panll.assist.dto.UserSettings; | ||
| 14 | -import top.panll.assist.utils.PageInfo; | ||
| 15 | - | ||
| 16 | -import javax.servlet.http.HttpServletRequest; | ||
| 17 | -import javax.servlet.http.HttpServletResponse; | ||
| 18 | -import java.io.BufferedOutputStream; | ||
| 19 | -import java.io.File; | ||
| 20 | -import java.io.IOException; | ||
| 21 | -import java.io.RandomAccessFile; | ||
| 22 | -import java.nio.charset.StandardCharsets; | ||
| 23 | -import java.util.List; | ||
| 24 | -import java.util.Map; | ||
| 25 | - | ||
| 26 | -@Controller | ||
| 27 | -@RequestMapping("/download") | ||
| 28 | -public class DownloadController { | ||
| 29 | - | ||
| 30 | - private final static Logger logger = LoggerFactory.getLogger(DownloadController.class); | ||
| 31 | - | ||
| 32 | - @Autowired | ||
| 33 | - private UserSettings userSettings; | ||
| 34 | - | ||
| 35 | - /** | ||
| 36 | - * 获取app+stream列表 | ||
| 37 | - * | ||
| 38 | - * @return | ||
| 39 | - */ | ||
| 40 | - @GetMapping(value = "/**") | ||
| 41 | - @ResponseBody | ||
| 42 | - public void download(HttpServletRequest request, HttpServletResponse response) { | ||
| 43 | - | ||
| 44 | - String resourcePath = request.getServletPath(); | ||
| 45 | - System.out.println(resourcePath); | ||
| 46 | - resourcePath = resourcePath.substring("/download".length() + 1, resourcePath.length()); | ||
| 47 | - String record = userSettings.getRecord(); | ||
| 48 | -// if (record.endsWith("/")) { | ||
| 49 | -// record = record.substring(0, record.length() - 1); | ||
| 50 | -// System.out.println(record); | 1 | +//package top.panll.assist.controller; |
| 2 | +// | ||
| 3 | +// | ||
| 4 | +//import io.swagger.v3.oas.annotations.Operation; | ||
| 5 | +//import io.swagger.v3.oas.annotations.Parameter; | ||
| 6 | +//import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 7 | +//import org.apache.catalina.connector.ClientAbortException; | ||
| 8 | +//import org.slf4j.Logger; | ||
| 9 | +//import org.slf4j.LoggerFactory; | ||
| 10 | +//import org.springframework.beans.factory.annotation.Autowired; | ||
| 11 | +//import org.springframework.stereotype.Controller; | ||
| 12 | +//import org.springframework.web.bind.annotation.*; | ||
| 13 | +//import top.panll.assist.dto.UserSettings; | ||
| 14 | +//import top.panll.assist.utils.PageInfo; | ||
| 15 | +// | ||
| 16 | +//import javax.servlet.http.HttpServletRequest; | ||
| 17 | +//import javax.servlet.http.HttpServletResponse; | ||
| 18 | +//import java.io.BufferedOutputStream; | ||
| 19 | +//import java.io.File; | ||
| 20 | +//import java.io.IOException; | ||
| 21 | +//import java.io.RandomAccessFile; | ||
| 22 | +//import java.nio.charset.StandardCharsets; | ||
| 23 | +//import java.util.List; | ||
| 24 | +//import java.util.Map; | ||
| 25 | +// | ||
| 26 | +//@Controller | ||
| 27 | +//@RequestMapping("/download") | ||
| 28 | +//public class DownloadController { | ||
| 29 | +// | ||
| 30 | +// private final static Logger logger = LoggerFactory.getLogger(DownloadController.class); | ||
| 31 | +// | ||
| 32 | +// @Autowired | ||
| 33 | +// private UserSettings userSettings; | ||
| 34 | +// | ||
| 35 | +// /** | ||
| 36 | +// * 获取app+stream列表 | ||
| 37 | +// * | ||
| 38 | +// * @return | ||
| 39 | +// */ | ||
| 40 | +// @GetMapping(value = "/**") | ||
| 41 | +// @ResponseBody | ||
| 42 | +// public void download(HttpServletRequest request, HttpServletResponse response) { | ||
| 43 | +// | ||
| 44 | +// String resourcePath = request.getServletPath(); | ||
| 45 | +// System.out.println(resourcePath); | ||
| 46 | +// resourcePath = resourcePath.substring("/download".length() + 1, resourcePath.length()); | ||
| 47 | +// String record = userSettings.getRecord(); | ||
| 48 | +//// if (record.endsWith("/")) { | ||
| 49 | +//// record = record.substring(0, record.length() - 1); | ||
| 50 | +//// System.out.println(record); | ||
| 51 | +//// } | ||
| 52 | +// System.out.println(record + resourcePath); | ||
| 53 | +// File file = new File(record + resourcePath); | ||
| 54 | +// if (!file.exists()) { | ||
| 55 | +// response.setStatus(HttpServletResponse.SC_NOT_FOUND); | ||
| 56 | +// return; | ||
| 51 | // } | 57 | // } |
| 52 | - System.out.println(record + resourcePath); | ||
| 53 | - File file = new File(record + resourcePath); | ||
| 54 | - if (!file.exists()) { | ||
| 55 | - response.setStatus(HttpServletResponse.SC_NOT_FOUND); | ||
| 56 | - return; | ||
| 57 | - } | ||
| 58 | - | ||
| 59 | - /** | ||
| 60 | - * 参考实现来自: CSDN 进修的CODER SpringBoot Java实现Http方式分片下载断点续传+实现H5大视频渐进式播放 | ||
| 61 | - * https://blog.csdn.net/lovequanquqn/article/details/104562945 | ||
| 62 | - */ | ||
| 63 | - String range = request.getHeader("Range"); | ||
| 64 | - logger.info("current request rang:" + range); | ||
| 65 | - //开始下载位置 | ||
| 66 | - long startByte = 0; | ||
| 67 | - //结束下载位置 | ||
| 68 | - long endByte = file.length() - 1; | ||
| 69 | - logger.info("文件开始位置:{},文件结束位置:{},文件总长度:{}", startByte, endByte, file.length()); | ||
| 70 | - | ||
| 71 | - //有range的话 | ||
| 72 | - if (range != null && range.contains("bytes=") && range.contains("-")) { | ||
| 73 | - range = range.substring(range.lastIndexOf("=") + 1).trim(); | ||
| 74 | - String[] ranges = range.split("-"); | ||
| 75 | - try { | ||
| 76 | - //判断range的类型 | ||
| 77 | - if (ranges.length == 1) { | ||
| 78 | - // 类型一:bytes=-2343, | ||
| 79 | - if (range.startsWith("-")) { | ||
| 80 | - endByte = Long.parseLong(ranges[0]); | ||
| 81 | - } | ||
| 82 | - //类型二:bytes=2343- | ||
| 83 | - else if (range.endsWith("-")) { | ||
| 84 | - startByte = Long.parseLong(ranges[0]); | ||
| 85 | - } | ||
| 86 | - } | ||
| 87 | - //类型三:bytes=22-2343 | ||
| 88 | - else if (ranges.length == 2) { | ||
| 89 | - startByte = Long.parseLong(ranges[0]); | ||
| 90 | - endByte = Long.parseLong(ranges[1]); | ||
| 91 | - } | ||
| 92 | - | ||
| 93 | - } catch (NumberFormatException e) { | ||
| 94 | - startByte = 0; | ||
| 95 | - endByte = file.length() - 1; | ||
| 96 | - logger.error("Range Occur Error,Message:{}", e.getLocalizedMessage()); | ||
| 97 | - } | ||
| 98 | - | ||
| 99 | - | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - // 要下载的长度 | ||
| 103 | - long contentLength = endByte - startByte + 1; | ||
| 104 | - // 文件名 | ||
| 105 | - String fileName = file.getName(); | ||
| 106 | - // 文件类型 | ||
| 107 | - String contentType = request.getServletContext().getMimeType(fileName); | ||
| 108 | - | ||
| 109 | - // 解决下载文件时文件名乱码问题 | ||
| 110 | - byte[] fileNameBytes = fileName.getBytes(StandardCharsets.UTF_8); | ||
| 111 | - fileName = new String(fileNameBytes, 0, fileNameBytes.length, StandardCharsets.ISO_8859_1); | ||
| 112 | - | ||
| 113 | - response.setHeader("Content-Type", contentType); | ||
| 114 | - response.setHeader("Content-Length", String.valueOf(contentLength)); | ||
| 115 | - //inline表示浏览器直接使用,attachment表示下载,fileName表示下载的文件名 | ||
| 116 | - response.setHeader("Content-Disposition", "inline;filename=" + fileName); | ||
| 117 | - response.setContentType(contentType); | ||
| 118 | - if (range != null) { | ||
| 119 | - //各种响应头设置 | ||
| 120 | - //支持断点续传,获取部分字节内容: | ||
| 121 | - response.setHeader("Accept-Ranges", "bytes"); | ||
| 122 | - //http状态码要为206:表示获取部分内容 | ||
| 123 | - response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); | ||
| 124 | - // Content-Range,格式为:[要下载的开始位置]-[结束位置]/[文件总大小] | ||
| 125 | - response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + file.length()); | ||
| 126 | - } else { | ||
| 127 | - response.setStatus(HttpServletResponse.SC_OK); | ||
| 128 | - } | ||
| 129 | - | ||
| 130 | - | ||
| 131 | - BufferedOutputStream outputStream = null; | ||
| 132 | - RandomAccessFile randomAccessFile = null; | ||
| 133 | - //已传送数据大小 | ||
| 134 | - long transmitted = 0; | ||
| 135 | - try { | ||
| 136 | - randomAccessFile = new RandomAccessFile(file, "r"); | ||
| 137 | - | ||
| 138 | - outputStream = new BufferedOutputStream(response.getOutputStream()); | ||
| 139 | - byte[] buff = new byte[4096]; | ||
| 140 | - int len = 0; | ||
| 141 | - randomAccessFile.seek(startByte); | ||
| 142 | - //warning:判断是否到了最后不足4096(buff的length)个byte这个逻辑((transmitted + len) <= contentLength)要放前面 | ||
| 143 | - //不然会会先读取randomAccessFile,造成后面读取位置出错; | ||
| 144 | - while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buff)) != -1) { | ||
| 145 | - outputStream.write(buff, 0, len); | ||
| 146 | - transmitted += len; | ||
| 147 | - } | ||
| 148 | - //处理不足buff.length部分 | ||
| 149 | - if (transmitted < contentLength) { | ||
| 150 | - len = randomAccessFile.read(buff, 0, (int) (contentLength - transmitted)); | ||
| 151 | - outputStream.write(buff, 0, len); | ||
| 152 | - transmitted += len; | ||
| 153 | - } | ||
| 154 | - | ||
| 155 | - outputStream.flush(); | ||
| 156 | - response.flushBuffer(); | ||
| 157 | - randomAccessFile.close(); | ||
| 158 | - logger.info("下载完毕:" + startByte + "-" + endByte + ":" + transmitted); | ||
| 159 | - } catch (ClientAbortException e) { | ||
| 160 | - logger.warn("用户停止下载:" + startByte + "-" + endByte + ":" + transmitted); | ||
| 161 | - //捕获此异常表示拥护停止下载 | ||
| 162 | - } catch (IOException e) { | ||
| 163 | - e.printStackTrace(); | ||
| 164 | - logger.error("用户下载IO异常,Message:{}", e.getLocalizedMessage()); | ||
| 165 | - } finally { | ||
| 166 | - try { | ||
| 167 | - if (randomAccessFile != null) { | ||
| 168 | - randomAccessFile.close(); | ||
| 169 | - } | ||
| 170 | - } catch (IOException e) { | ||
| 171 | - e.printStackTrace(); | ||
| 172 | - } | ||
| 173 | - }///end try | ||
| 174 | - } | ||
| 175 | -} | 58 | +// |
| 59 | +// /** | ||
| 60 | +// * 参考实现来自: CSDN 进修的CODER SpringBoot Java实现Http方式分片下载断点续传+实现H5大视频渐进式播放 | ||
| 61 | +// * https://blog.csdn.net/lovequanquqn/article/details/104562945 | ||
| 62 | +// */ | ||
| 63 | +// String range = request.getHeader("Range"); | ||
| 64 | +// logger.info("current request rang:" + range); | ||
| 65 | +// //开始下载位置 | ||
| 66 | +// long startByte = 0; | ||
| 67 | +// //结束下载位置 | ||
| 68 | +// long endByte = file.length() - 1; | ||
| 69 | +// logger.info("文件开始位置:{},文件结束位置:{},文件总长度:{}", startByte, endByte, file.length()); | ||
| 70 | +// | ||
| 71 | +// //有range的话 | ||
| 72 | +// if (range != null && range.contains("bytes=") && range.contains("-")) { | ||
| 73 | +// range = range.substring(range.lastIndexOf("=") + 1).trim(); | ||
| 74 | +// String[] ranges = range.split("-"); | ||
| 75 | +// try { | ||
| 76 | +// //判断range的类型 | ||
| 77 | +// if (ranges.length == 1) { | ||
| 78 | +// // 类型一:bytes=-2343, | ||
| 79 | +// if (range.startsWith("-")) { | ||
| 80 | +// endByte = Long.parseLong(ranges[0]); | ||
| 81 | +// } | ||
| 82 | +// //类型二:bytes=2343- | ||
| 83 | +// else if (range.endsWith("-")) { | ||
| 84 | +// startByte = Long.parseLong(ranges[0]); | ||
| 85 | +// } | ||
| 86 | +// } | ||
| 87 | +// //类型三:bytes=22-2343 | ||
| 88 | +// else if (ranges.length == 2) { | ||
| 89 | +// startByte = Long.parseLong(ranges[0]); | ||
| 90 | +// endByte = Long.parseLong(ranges[1]); | ||
| 91 | +// } | ||
| 92 | +// | ||
| 93 | +// } catch (NumberFormatException e) { | ||
| 94 | +// startByte = 0; | ||
| 95 | +// endByte = file.length() - 1; | ||
| 96 | +// logger.error("Range Occur Error,Message:{}", e.getLocalizedMessage()); | ||
| 97 | +// } | ||
| 98 | +// | ||
| 99 | +// | ||
| 100 | +// } | ||
| 101 | +// | ||
| 102 | +// // 要下载的长度 | ||
| 103 | +// long contentLength = endByte - startByte + 1; | ||
| 104 | +// // 文件名 | ||
| 105 | +// String fileName = file.getName(); | ||
| 106 | +// // 文件类型 | ||
| 107 | +// String contentType = request.getServletContext().getMimeType(fileName); | ||
| 108 | +// | ||
| 109 | +// // 解决下载文件时文件名乱码问题 | ||
| 110 | +// byte[] fileNameBytes = fileName.getBytes(StandardCharsets.UTF_8); | ||
| 111 | +// fileName = new String(fileNameBytes, 0, fileNameBytes.length, StandardCharsets.ISO_8859_1); | ||
| 112 | +// | ||
| 113 | +// response.setHeader("Content-Type", contentType); | ||
| 114 | +// response.setHeader("Content-Length", String.valueOf(contentLength)); | ||
| 115 | +// //inline表示浏览器直接使用,attachment表示下载,fileName表示下载的文件名 | ||
| 116 | +// response.setHeader("Content-Disposition", "inline;filename=" + fileName); | ||
| 117 | +// response.setContentType(contentType); | ||
| 118 | +// if (range != null) { | ||
| 119 | +// //各种响应头设置 | ||
| 120 | +// //支持断点续传,获取部分字节内容: | ||
| 121 | +// response.setHeader("Accept-Ranges", "bytes"); | ||
| 122 | +// //http状态码要为206:表示获取部分内容 | ||
| 123 | +// response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); | ||
| 124 | +// // Content-Range,格式为:[要下载的开始位置]-[结束位置]/[文件总大小] | ||
| 125 | +// response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + file.length()); | ||
| 126 | +// } else { | ||
| 127 | +// response.setStatus(HttpServletResponse.SC_OK); | ||
| 128 | +// } | ||
| 129 | +// | ||
| 130 | +// | ||
| 131 | +// BufferedOutputStream outputStream = null; | ||
| 132 | +// RandomAccessFile randomAccessFile = null; | ||
| 133 | +// //已传送数据大小 | ||
| 134 | +// long transmitted = 0; | ||
| 135 | +// try { | ||
| 136 | +// randomAccessFile = new RandomAccessFile(file, "r"); | ||
| 137 | +// | ||
| 138 | +// outputStream = new BufferedOutputStream(response.getOutputStream()); | ||
| 139 | +// byte[] buff = new byte[4096]; | ||
| 140 | +// int len = 0; | ||
| 141 | +// randomAccessFile.seek(startByte); | ||
| 142 | +// //warning:判断是否到了最后不足4096(buff的length)个byte这个逻辑((transmitted + len) <= contentLength)要放前面 | ||
| 143 | +// //不然会会先读取randomAccessFile,造成后面读取位置出错; | ||
| 144 | +// while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buff)) != -1) { | ||
| 145 | +// outputStream.write(buff, 0, len); | ||
| 146 | +// transmitted += len; | ||
| 147 | +// } | ||
| 148 | +// //处理不足buff.length部分 | ||
| 149 | +// if (transmitted < contentLength) { | ||
| 150 | +// len = randomAccessFile.read(buff, 0, (int) (contentLength - transmitted)); | ||
| 151 | +// outputStream.write(buff, 0, len); | ||
| 152 | +// transmitted += len; | ||
| 153 | +// } | ||
| 154 | +// | ||
| 155 | +// outputStream.flush(); | ||
| 156 | +// response.flushBuffer(); | ||
| 157 | +// randomAccessFile.close(); | ||
| 158 | +// logger.info("下载完毕:" + startByte + "-" + endByte + ":" + transmitted); | ||
| 159 | +// } catch (ClientAbortException e) { | ||
| 160 | +// logger.warn("用户停止下载:" + startByte + "-" + endByte + ":" + transmitted); | ||
| 161 | +// //捕获此异常表示拥护停止下载 | ||
| 162 | +// } catch (IOException e) { | ||
| 163 | +// e.printStackTrace(); | ||
| 164 | +// logger.error("用户下载IO异常,Message:{}", e.getLocalizedMessage()); | ||
| 165 | +// } finally { | ||
| 166 | +// try { | ||
| 167 | +// if (randomAccessFile != null) { | ||
| 168 | +// randomAccessFile.close(); | ||
| 169 | +// } | ||
| 170 | +// } catch (IOException e) { | ||
| 171 | +// e.printStackTrace(); | ||
| 172 | +// } | ||
| 173 | +// }///end try | ||
| 174 | +// } | ||
| 175 | +//} |