java 使用 aws s3 sdk 通过分段下载来实现 html 页面 video 的断点播放、拖动进度播放

参考博客:

  1. Java 视频流分段返回

  2. java 拉取 aws s3 视频流返回给浏览器:

    复制代码
     @Override
     public void playVideo(Long fileLength,String key,HttpServletRequest request,HttpServletResponse response) {
         OutputStream outputStream = null;
         S3ObjectInputStream s3ObjectInputStream = null;
         try {
             outputStream = response.getOutputStream();
    
             // 获取从哪个字节开始读取文件
             String rangeHeader = request.getHeader("Range");
             if (rangeHeader == null) {
                 response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Required 'Range' header is missing");
                 return;
             }
    
             long startByte = 0;
             long endByte = fileLength - 1;
             if (rangeHeader.startsWith("bytes=")) {
                 String[] rangeParts = rangeHeader.substring(6).split("-");
                 startByte = Long.parseLong(rangeParts[0]);
                 endByte = rangeParts.length > 1 ? startByte : fileLength - 1;
             }
    
             if (startByte > endByte || startByte < 0 || endByte >= fileLength) {
                 response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, "Requested range not satisfiable");
                 return;
             }
    
             // 设置响应头
             response.setHeader("Content-Type", "video/mp4");
             response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
             response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + fileLength);
    
             // 从S3获取视频流
             GetObjectRequest getObjectRequest = new GetObjectRequest(this.fileBucket, key).withRange(startByte, endByte + 1);
             S3Object s3Object = this.amazonS3.getObject(getObjectRequest);
             s3ObjectInputStream = s3Object.getObjectContent();
    
             // 将视频流响应给客户端
             byte[] buffer = new byte[1024 * 1024]; // 使用1MB的缓冲区
             int bytesRead;
             long totalBytesRead = 0;
             while ((bytesRead = s3ObjectInputStream.read(buffer)) != -1 && totalBytesRead <= (endByte - startByte + 1)) {
                 outputStream.write(buffer, 0, bytesRead);
                 totalBytesRead += bytesRead;
             }
             response.setContentLength((int) (endByte - startByte + 1)); // 设置实际返回的数据长度
         } catch (Exception e) {
             // 处理异常
             log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
             // 处理异常
             try {
                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal server error");
             }catch ( Exception e ){
                 log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
             }
         }finally {
             if( outputStream != null ){
                 try {
                     outputStream.close();
                 }catch ( Exception e1 ){
                     log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e1 );
                 }
             }
             if( s3ObjectInputStream != null ){
                 try {
                     s3ObjectInputStream.close();
                 }catch ( Exception e1 ){
                     log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e1 );
                 }
             }
         }
     }
  3. java读物本地视频文件返回给浏览器:

java 复制代码
    @Override
    public void playVideo(HttpServletRequest request, HttpServletResponse response) {
        RandomAccessFile randomAccessFile = null;
        OutputStream outputStream = null;
        try {
            response.reset();
            // File file = new File("D:\\素材\\视频\\crazy_max_small.mp4");
            File file = new File("D:\\视频\\888.mp4");
            long fileLength = file.length();
            // 随机读文件
            randomAccessFile = new RandomAccessFile(file, "r");

            //获取从那个字节开始读取文件
            String rangeString = request.getHeader("Range");
            System.out.println(rangeString);
            long range=0;
            if (rangeString!=null) {
                range = Long.valueOf(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
            }
            //获取响应的输出流
            outputStream = response.getOutputStream();
            //设置内容类型
            response.setHeader("Content-Type", "video/mp4");
            //返回码需要为206,代表只处理了部分请求,响应了部分数据
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

            // 移动访问指针到指定位置
            randomAccessFile.seek(range);
            // 每次请求只返回1MB的视频流
            byte[] bytes = new byte[1024 * 1024];
            int len = randomAccessFile.read(bytes);
            //设置此次相应返回的数据长度
            response.setContentLength(len);
            //设置此次相应返回的数据范围
            response.setHeader("Content-Range", "bytes "+range+"-"+(fileLength-1)+"/"+fileLength);
            // 将这1MB的视频流响应给客户端
            outputStream.write(bytes, 0, len);
            System.out.println("返回数据区间:【"+range+"-"+(range+len)+"】");
        }catch ( Exception e ){
            log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
            // 处理异常
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal server error");
            }catch ( Exception e1 ){
                log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e1 );
            }
        }finally {
            if( outputStream != null){
                try {
                    outputStream.close();
                }catch ( Exception e){
                    log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
                }
            }
            if( randomAccessFile != null ){
                try {
                    randomAccessFile.close();
                }catch ( Exception e ){
                    log.error( CommonConstans.EXCEPTION_LOG_PREFIX,e );
                }
            }
        }
相关推荐
橘猫云计算机设计1 小时前
基于Springboot的自习室预约系统的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
java·spring boot·后端·毕业设计
秋书一叶2 小时前
SpringBoot项目打包为window安装包
java·spring boot·后端
碎梦归途2 小时前
23种设计模式-结构型模式之外观模式(Java版本)
java·开发语言·jvm·设计模式·intellij-idea·外观模式
极客先躯2 小时前
高级java每日一道面试题-2025年4月13日-微服务篇[Nacos篇]-Nacos如何处理网络分区情况下的服务可用性问题?
java·服务器·网络·微服务·nacos·高级面试
pwzs3 小时前
Spring MVC 执行流程全解析:从请求到响应的七步走
java·后端·spring·spring mvc
我该如何取个名字3 小时前
Mac配置Java的环境变量
java·开发语言·macos
kkkkatoq3 小时前
Java中的锁
java·开发语言
界面开发小八哥4 小时前
「Java EE开发指南」用MyEclipse开发EJB 3无状态会话Bean(二)
java·ide·java-ee·eclipse·myeclipse
LCY1334 小时前
spring security +kotlin 实现oauth2.0 认证
java·spring·kotlin
soulermax4 小时前
数字ic后端设计从入门到精通2(含fusion compiler, tcl教学)
java·linux·服务器