• java 视频断点播放,实现无卡顿


    背景:前端一般展示页面播放视频都是从头加载视频,这样会使用户体现很差,网速不好快进的话就会卡顿,加载太慢,为了 解决这一问题,后台给前端提供的接口需要实现断点下载的功能

    1. @ApiOperation(value = "视频播放一方法", notes = "视频播放一方法")
    2. @GetMapping(value = "/preview")
    3. @ApiImplicitParams({
    4. @ApiImplicitParam(name = "url", value = "文件路径", required = true,paramType = "query")
    5. })
    6. @Override
    7. public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    8. // TODO Auto-generated method stub
    9. HttpServletRequest request=(HttpServletRequest)req;
    10. HttpServletResponse response=(HttpServletResponse)res;
    11. System.out.println(req.getParameter("url"));
    12. String path = addMinioUrl.getObPath(req.getParameter("url"));
    13. //String path="E:/video/111.mp4";
    14. RandomAccessFile targetFile = null;
    15. OutputStream outputStream = null;
    16. try {
    17. outputStream = response.getOutputStream();
    18. response.reset();
    19. //获取请求头中Range的值
    20. String rangeString = request.getHeader(HttpHeaders.RANGE);
    21. //打开文件
    22. File file = new File(path);
    23. if (file.exists()) {
    24. //使用RandomAccessFile读取文件
    25. targetFile = new RandomAccessFile(file, "r");
    26. long fileLength = targetFile.length();
    27. long requestSize = (int) fileLength;
    28. //分段下载视频
    29. if (StringUtils.hasText(rangeString)) {
    30. //从Range中提取需要获取数据的开始和结束位置
    31. long requestStart = 0, requestEnd = 0;
    32. String[] ranges = rangeString.split("=");
    33. if (ranges.length > 1) {
    34. String[] rangeDatas = ranges[1].split("-");
    35. requestStart = Integer.parseInt(rangeDatas[0]);
    36. if (rangeDatas.length > 1) {
    37. requestEnd = Integer.parseInt(rangeDatas[1]);
    38. }
    39. }
    40. if (requestEnd != 0 && requestEnd > requestStart) {
    41. requestSize = requestEnd - requestStart + 1;
    42. }
    43. //根据协议设置请求头
    44. response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
    45. response.setHeader(HttpHeaders.CONTENT_TYPE, "video/mp4");
    46. if (!StringUtils.hasText(rangeString)) {
    47. response.setHeader(HttpHeaders.CONTENT_LENGTH, fileLength + "");
    48. } else {
    49. long length;
    50. if (requestEnd > 0) {
    51. length = requestEnd - requestStart + 1;
    52. response.setHeader(HttpHeaders.CONTENT_LENGTH, "" + length);
    53. response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + requestStart + "-" + requestEnd + "/" + fileLength);
    54. } else {
    55. length = fileLength - requestStart;
    56. response.setHeader(HttpHeaders.CONTENT_LENGTH, "" + length);
    57. response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + requestStart + "-" + (fileLength - 1) + "/"
    58. + fileLength);
    59. }
    60. }
    61. //断点传输下载视频返回206
    62. response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
    63. //设置targetFile,从自定义位置开始读取数据
    64. targetFile.seek(requestStart);
    65. } else {
    66. //如果Range为空则下载整个视频
    67. response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=test.mp4");
    68. //设置文件长度
    69. response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileLength));
    70. }
    71. //从磁盘读取数据流返回
    72. byte[] cache = new byte[4096];
    73. try {
    74. while (requestSize > 0) {
    75. int len = targetFile.read(cache);
    76. if (requestSize < cache.length) {
    77. outputStream.write(cache, 0, (int) requestSize);
    78. } else {
    79. outputStream.write(cache, 0, len);
    80. if (len < cache.length) {
    81. break;
    82. }
    83. }
    84. requestSize -= cache.length;
    85. }
    86. } catch (IOException e) {
    87. // tomcat原话。写操作IO异常几乎总是由于客户端主动关闭连接导致,所以直接吃掉异常打日志
    88. //比如使用video播放视频时经常会发送Range为0- 的范围只是为了获取视频大小,之后就中断连接了
    89. //log.info(e.getMessage());
    90. //throw new RuntimeException(e);
    91. }
    92. } else {
    93. // throw new RuntimeException("文件路劲有误");
    94. }
    95. outputStream.flush();
    96. } catch (Exception e) {
    97. // e.printStackTrace();
    98. // throw new RuntimeException("文件传输错误");
    99. }finally {
    100. if(outputStream != null){
    101. try {
    102. outputStream.close();
    103. } catch (IOException e) {
    104. //e.printStackTrace();
    105. // throw new RuntimeException("流释放错误");
    106. }
    107. }
    108. if(targetFile != null){
    109. try {
    110. targetFile.close();
    111. } catch (IOException e) {
    112. //e.printStackTrace();
    113. // throw new RuntimeException("文件流释放错误");
    114. }
    115. }
    116. }
    117. }

    前端要安装视频插件,需要的小伙伴可以@我

  • 相关阅读:
    注解方式优雅的实现 Redisson 分布式锁
    java计算机毕业设计无人智慧药柜系统设计源码+lw文档+系统+数据库
    Android busybox介绍
    XILINX XC7A200T-2FBG676C PLC可编程逻辑控制器
    CentOS上如何配置手动和定时任务自动进行时间同步
    mysql基础知识篇(六)
    Python运算符优先级
    数据结构习题--杨辉三角形(返回某一行)
    如何在 Android Studio 中重命名软件包名称
    数据库基础面试第二弹
  • 原文地址:https://blog.csdn.net/weixin_41018853/article/details/128083233