vue-video-player,springboot实现视频分段下载播放

本文介绍了如何通过HTTP请求中的Range和Content-Range头实现视频的大文件分段加载,包括前端使用标准标签及vue-video-player插件的方法,以及后端如何响应前端请求返回指定区间的视频数据。

事情的起因是在博主把项目部署到服务器上后,发现由于视频太大,加上服务器太垃圾,导致稍微大点的视频加载很久才能播放(指十多分钟…),然后就上网查找资料,看下咋实现。
这里涉及到有关http请求的知识“HTTP Header里的Range和Content-Range参数,Range是在请求头里
在这里插入图片描述
Range: bytes=start-end,这个表示告诉后端只获取start到end字节的文件
在这里插入图片描述
Content-Range: bytes start-end/文件总大小,返回浏览器,说明这次请求到的文件范围,还有总文件的大小;

晓得这两个Http参数就好办了,大体上是浏览器发送带有Range的请求头,然后后端根据Range指定的范围返回对应大小的文件片段

#1.使用标签

标签的src属性指向服务器链接,当服务器响应的HTTP状态码为206时,浏览器会自动开启分段式播放,在每次的HTTP请求头中自动加入Range请求头,服务端只需要根据前端传过来的Range信息截取视频的指定区间来响应即可。

#2.使用vue-video-player(vue里面的插件)

一开始我发现vue-video-player和video不同,每次发送的请求的Range里都只有字节开始,没有字节结尾!在这里插入图片描述
以至于找了很久怎么改vue-video-player请求头的参数,后来发现只要有开头就能够实现断点下载,vue-video-player下次的Range开头一定是上一次的开头加上返回头里content-length(后台返回的下载文件的大小,response自动返回的,不用自己设置)
大体流程是vue-video-player发送断点请求,Range:btyes=start-,后台知道是断点请求后,利用RandomAccessFile类里的seek(start)方法将读取文件的光标移到Range指定位置,然后自己设置每次返回文件大小,返回给前端
前端看response里状态代码是206,就会自动发送后续请求,Range:bytes=start+content-length里的字节大小

代码

vue-video-player

<template>
  <div class="my_video">
    <video-player  class="video-player vjs-custom-skin"
                   ref="videoPlayer"
                   :playsinline="true"
                   :options="videoPlayerOptions"
                   @play="onPlayerPlay($event)"
                   @pause="onPlayerPause($event)"
                   @ended="onPlayerEnded($event)"
                   @waiting="onPlayerWaiting($event)"
                   @playing="onPlayerPlaying($event)"
                   @loadeddata="onPlayerLoadeddata($event)"
                   @timeupdate="onPlayerTimeupdate($event)"
                   @canplay="onPlayerCanplay($event)"
                   @canplaythrough="onPlayerCanplaythrough($event)"
                   @statechanged="playerStateChanged($event)"
                   @ready="playerReadied"
    ></video-player>

  </div>
</template>

<script>
// 导入组件
import {videoPlayer} from 'vue-video-player'

export default {
  name: 'VideoPlayer',
  props:{
    videoStr:{default:""}
  },
  components: {
    videoPlayer
  },
  data () {
    return {
      video:'',         //具体视频
      fileType: 'mp4', // 资源的类型
      videoUrl: '',	// 资源的路径地址
      posterUrl:'',  //封面地址
      headers:{
        'Range':'bytes=0-1000'
      }
    }
  },
  mounted(){

  },
  methods:{
    // 播放回调
    onPlayerPlay(player) {
      console.log('player play!', player)
    },

    // 暂停回调
    onPlayerPause(player) {
      console.log('player pause!', player)
    },

    // 视频播完回调
    onPlayerEnded($event) {
      // this.$refs.videoPlayer.player.src(this.fileUrl)
      $event
    },

    // DOM元素上的readyState更改导致播放停止
    onPlayerWaiting($event) {
      console.log($event)
    },

    // 已开始播放回调
    onPlayerPlaying($event) {
      console.log($event)
    },

    // 当播放器在当前播放位置下载数据时触发
    onPlayerLoadeddata($event) {
      console.log($event)
    },

    // 当前播放位置发生变化时触发。
    onPlayerTimeupdate($event) {
      console.log($event)
    },

    //媒体的readyState为HAVE_FUTURE_DATA或更高
    onPlayerCanplay(player) {
      console.log('player Canplay!', player)
    },

    //媒体的readyState为HAVE_ENOUGH_DATA或更高。这意味着可以在不缓冲的情况下播放整个媒体文件。
    onPlayerCanplaythrough(player) {
      console.log('player Canplaythrough!', player)
    },

    //播放状态改变回调
    playerStateChanged(playerCurrentState) {
      console.log('player current update state', playerCurrentState)
    },

    //将侦听器绑定到组件的就绪状态。与事件监听器的不同之处在于,如果ready事件已经发生,它将立即触发该函数。。
    playerReadied(player) {
      console.log('example player 1 readied', player);

    },
  },
  computed: {
    videoPlayerOptions () {
      const videoPlayerOptions = {
        playbackRates: [0.75, 1.0, 1.25, 1.5,2.0], //播放速度
        autoplay: false, // 是否自动播放。
        muted: false, // 是否静音播放,默认情况下将会消除任何音频。
        loop: false, // 是否循环播放。
        preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
        language: 'zh-CN',
        aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
        fluid: true, // 是否流体从而按比例缩放以适应其容器。
        flash:{hls:{withCreadentials:false}},//可以播放rtmp视频
        html5:{hls:{withCreadentials:false}},//可以播放m3u8视频
        sources: [{
          type: 'video/' + this.fileType,
          src:this.videoStr, // 视频url地址

        }],
        poster: this.posterUrl, // 封面地址
        width: '100%',
        notSupportedMessage: '此视频暂无法播放...', // 当无法播放时允许覆盖Video.js,显示的默认信息。
        controlBar: {
          timeDivider: true,
          durationDisplay: true,
          remainingTimeDisplay: false,
          fullscreenToggle: true
        }
      }
      return videoPlayerOptions
    }
  },
}
</script>
<style scoped >
.my_video{
  width: 100%;
  height: 60%;

}
.video-js .vjs-big-play-button{
  /*对播放按钮的样式进行设置*/
  width: 100%;
  height: 100%;
  border-radius: 50%;
}
</style>

后端:


```javascript

```java
@RequestMapping("downVideoByStep")
    public void play(String flag, HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("downVideoByStep");
        String basePath = System.getProperty("user.dir") + "/video/";
                response.reset();
                File file = new File(basePath+flag);
                long fileLength = file.length();
                // 随机读文件
                RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");

                //获取从那个字节开始读取文件
                String rangeString = request.getHeader("Range");
                long range=0;
                if (StrUtil.isNotBlank(rangeString)) {
                    range = Long.valueOf(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
                }
                //获取响应的输出流
                OutputStream 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);
                outputStream.close();
                randomAccessFile.close();

                System.out.println("返回数据区间:【"+range+"-"+(range+len)+"】");




    }

### 如何在Vue.js项目中使用vue-video-player实现分段播放视频 为了实现Vue.js项目中的分段播放视频功能,可以利用`vue-video-player`这一强大的工具来简化开发过程。虽然提供的参考资料并未直接提及`vue-video-player`的具体应用案例[^1],但是基于此库的功能特性以及Vue生态系统的灵活性,下面提供一种可行的技术方案。 #### 安装依赖包 首先,在项目根目录下通过npm安装必要的软件包: ```bash npm install vue-video-player --save ``` #### 配置全局引入 接着,在项目的入口文件main.js里配置全局组件加载方式: ```javascript // main.js import VideoPlayer from 'vue-video-player' require('video.js/dist/video-js.css') require('vue-video-player/src/custom-theme.css') Vue.use(VideoPlayer) ``` #### 创建自定义混合逻辑 考虑到不同片段之间的切换可能涉及到一些共通的操作流程,比如暂停当前正在播放的部分并准备下一个部分的数据源等动作,因此建议创建一个专门处理这些行为的mixin: ```javascript // common/js/segmented-play-mixin.js export const SegmentedPlayMixin = { data() { return { currentSegmentIndex: 0, segments: [ { src: '/path/to/first-part.mp4' }, { src: '/path/to/second-part.mp4' } ] }; }, methods: { playNextSegment(player) { player.pause(); this.currentSegmentIndex++; if (this.currentSegmentIndex >= this.segments.length){ console.log("All segments have been played."); return; } let nextSrc = this.segments[this.currentSegmentIndex].src; player.src(nextSrc); player.play(); } } }; ``` #### 组件内部集成 最后一步是在具体的页面或者组件内调用上述设置好的资源和服务。这里给出一段简单的HTML模板配合JavaScript代码作为示范: ```html <template> <div class="example"> <video-player ref="myVid" @ended="playNextSegment($refs.myVid)"> <!-- 默认显示第一个片段 --> <source :src="segments[currentSegmentIndex].src"/> </video-player> </div> </template> <script> import { SegmentedPlayMixin } from '../common/js/segmented-play-mixin'; export default { name: "ExampleComponent", mixins:[SegmentedPlayMixin], } </script> ``` 以上就是如何借助于`vue-video-player`插件及其周边技术栈构建起一套完整的分段播放机制的大致思路和技术要点介绍。值得注意的是实际应用场景可能会更加复杂多变,所以还需要开发者们根据具体需求灵活调整优化解决方案。
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值