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 );
                }
            }
        }
相关推荐
better_liang5 分钟前
每日Java面试场景题知识点之-TCP/IP协议栈与Socket编程
java·tcp/ip·计算机网络·网络编程·socket·面试题
niucloud-admin17 分钟前
java服务端——controller控制器
java·开发语言
To Be Clean Coder19 分钟前
【Spring源码】通过 Bean 工厂获取 Bean 的过程
java·后端·spring
Fortunate Chen25 分钟前
类与对象(下)
java·javascript·jvm
程序员水自流26 分钟前
【AI大模型第9集】Function Calling,让AI大模型连接外部世界
java·人工智能·llm
‿hhh29 分钟前
综合交通运行协调与应急指挥平台项目说明
java·ajax·npm·json·需求分析·个人开发·规格说明书
小徐Chao努力29 分钟前
【Langchain4j-Java AI开发】06-工具与函数调用
java·人工智能·python
无心水32 分钟前
【神经风格迁移:全链路压测】33、全链路监控与性能优化最佳实践:Java+Python+AI系统稳定性保障的终极武器
java·python·性能优化
萧曵 丶40 分钟前
Synchronized 详解及 JDK 版本优化
java·多线程·synchronized
夏幻灵1 小时前
JAVA基础:基本数据类型和引用数据类型
java·开发语言