Bootstrap

vue2播放视频和预览文件的组件以及使用方法

##文件预览组件

  1. 按照组件
        解决展示pdf的问题
    npm install pdfh5
    npm install  [email protected] --ignore-scripts
    npm install --save dommatrix     
    npm install --save web-streams-polyfill
    
    解决excel和docx预览的问题
    npm install @vue-office/docx [email protected]
    npm install @vue-office/excel [email protected]
    npm install @vue/composition-api  
    
  2. 使用方法
    1. 前端:
    页面引入组件:
    import VueOfficeDocx from '@vue-office/docx'
    import Pdfh5 from "pdfh5";
    import VueOfficeExcel from '@vue-office/excel'
    
    import '@vue-office/excel/lib/index.css'
    import '@vue-office/docx/lib/index.css'
    
    pdf预览:
    <div id="pdf" v-if="nowItem.fileFormat === '.pdf'" style="width: 100%; height: 100%; border: none;"></div>
    
    docx预览:
    <vue-office-docx
    :key="new Date().getTime()"
    v-if="nowItem.fileFormat === '.docx'"
    :src="preViewUrl"
    @rendered="rendered"
    />
    excel预览:
    <vue-office-excel
    :key="new Date().getTime()"
    v-if="nowItem.fileFormat === '.xls' || nowItem.fileFormat === '.xlsx'"
    :src="preViewUrl"
    @rendered="rendered"
    />
    txt预览:
    <iframe v-if="nowItem.fileFormat === '.txt'" :src="preViewUrl" style="width: 100%; height: 98%; border: none;"/>
    
     js部分:  
     downLoadLibrary(item.id).then(res => { //请求接口获取文件流
     if (item.fileFormat === '.txt') {
     this.preViewUrl = window.URL.createObjectURL(res) //将文件流转化为url
     this.loading = false;
     } else if (item.fileFormat === '.pdf') {
     let prePdfUrl = window.URL.createObjectURL(res) //将文件流转化为url
    //使用id  #pdf 获取pdfh5实例
     this.pdfh5 = new Pdfh5('#pdf', {
     pdfurl: prePdfUrl,
     pageNum: false, //不显示页码
     backTop: false,  //不显示回到顶部
     zoomEnable: false, //禁止缩放
     maxZoom: 1, //点击屏幕缩放1倍 也就是禁止缩放的
     });
     //完成
     let that = this;
     this.pdfh5.on('complete', function (status, msg, time) {
     that.rendered(); //渲染完成回调方法 可以自己定义业务逻辑
     });
     } else {
     this.preViewUrl = res //docx和excel
     }
     })
     请求接口:
     export function downLoadLibrary(id){
     return request({
     url: '/manager/library/downLoadLibrary/' + id,
     method: 'get',
     responseType: 'blob',
     })
     }
    
    
    1. 后端:这里就是service层的接口,根据id获取文件流并返回给前端,前端根据文件格式进行不同的处理。
    public void downLoadLibrary(Long id, HttpServletResponse response) {
        TInstructionResourceLibrary instructionResourceLibrary = this.selectInstructionResourceLibraryById(id);
        if (Objects.isNull(instructionResourceLibrary)) {
            throw new ServiceException("资源不存在");
        }
        String configByKey = sysConfigService.selectConfigByKey("sys.upload.file");
        String resourcePath = configByKey + instructionResourceLibrary.getResourcePath();
        File file = new File(resourcePath);
        if (!file.exists()) {
            throw new ServiceException("文件不存在:" + resourcePath);
        }
        try {
            if (instructionResourceLibrary.getFileFormat().equals(".txt")) {
                response.setContentType("text/plain");
            } else if (instructionResourceLibrary.getFileFormat().equals(".pdf")) {
                response.setContentType("application/pdf");
            } else {
                response.setContentType("application/octet-stream");
            }
            FileUtils.writeBytes(resourcePath, response.getOutputStream());
        } catch (Exception e) {
            throw new ServiceException("下载文件失败:" + e.getMessage());
        }
    }
    

视频预览组件

  1. 使用自带的video组件即可,无需额外的组件。
  2. 前端:
<video
          ref="video"
          :controls="true"
          controlslist="nodownload noplaybackrate disablepictureinpicture"
          :src="preViewUrl"
          webkit-playsinline="true"
          playsinline="true"
          x-webkit-airplay="allow"
          x5-playsinline
          style="width: 100%"
          @play="onPlayerPlay"
          @pause="onPlayerPause"
          @seeking="seeking"
          @seeked="seeked"
          @canplay="onPlayerCanPlay"
          @timeupdate="onPlayerTimeUpdate"
          :autoplay="false"/>

js部分:

    playVideo(row) { // 打开视频弹出层 并且设置视频播放的src的url路径
      this.videoName = row.videoName;
      this.preViewOpen = true;
      this.preViewUrl = "http://域名:端口/路径"
      this.videoId = row.id;
    },
    handleClosePreView() { //关闭弹出层,并且清除播放记录 ,里面的字段可更具需要自定义
      console.log("关闭弹出层");
      this.addRecord("close")
      this.recordId = null; //播放记录id
      this.videoId = null; //播放的视频id

      this.currentTime = 0; //视频的当前播放进度时间
      this.tempTime = 0; //临时记录当前播放进度时间,防止拖动进度条时,时间不准确
      this.viewDuration = 0; //播放时长,单位秒,是根据开始播放到关闭弹出层的视频非暂停时间的总时长
      this.seconds = 0; //js计时器,大致计算播放时长 大概50多s 触发一次保存接口,之后清零
      this.setIntervalId && clearInterval(this.setIntervalId); // 清除定时器
      this.viewDurationInterval && clearInterval(this.viewDurationInterval); // 清除定时器
      this.duration = 0; //视频实际的时长
      this.videoName = ""; //视频每次
      this.$refs.video.load(); //重新加载视频
      this.preViewOpen = false; //关闭弹出层
    },
    //新增播放记录
    addRecord(option) { //要记录播放的时长
      let data = {
        "id": this.recordId, // 记录id
        "videoId": this.videoId, // 视频id
        "viewDuration": this.viewDuration, // 播放时长
      };
      addTrainVideoRecord(data).then(response => {
        console.log(response);
        this.recordId = response.msg;
        if (option === "close") {
          this.recordId = null;
        }
      });
    },
    onPlayerCanPlay() { //回调函数 视频准备好可以播放
        console.log("可以播放");
    },
    onPlayerPlay() { //回调函数 点击播放按钮,视频开始播放
      if (this.playerPauseFlag) {
        return;
      }
      //新增一条播放记录
      this.addRecord();
      this.setIntervalId = setInterval(() => {
        this.tempTime = this.currentTime + 0.01;
        this.seconds += 0.01;
      }, 1); // 定时器,每隔1ms获取一次播放时间
      this.viewDurationInterval = setInterval(() => {
        this.viewDuration += 1;
      }, 950)
    },
    onPlayerTimeUpdate(e) { //回调函数 视频播放进度变化 基本每秒触发一次
      this.currentTime = e.target.currentTime;
      this.duration = e.target.duration;
      //每播放1左右分钟秒保存一次播放记录
      if (this.seconds >= 140) {
        this.addRecord()
        this.seconds = 0;
      }
    },
    onPlayerPause() { //回调函数 视频暂停播放
      if (this.playerPauseFlag) {
        return;
      }
      //记录一次 下次开始是新的记录
      this.addRecord()
      this.setIntervalId && clearInterval(this.setIntervalId);
      this.viewDurationInterval && clearInterval(this.viewDurationInterval);
    },
    seeking() { //回调函数 拖动进度条
      console.log("拖动进度条");
    },
    seeked() { //回调函数 拖动进度条结束
      this.playerPauseFlag = true
      setTimeout(function () {
        this.playerPauseFlag = false
      }, 1000)
      // 只允许向后拖动
      if (this.tempTime < this.currentTime) {
        this.$refs.video.currentTime = this.tempTime;
      }
    },

后端接口:流式返回的 每次2m的视频流

@GetMapping(value = "/viewVideo/{id}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public void viewVideo(@PathVariable("id") Long id, HttpServletResponse response) throws IOException {
        // 获取视频地址
        File videoFile = videoService.findVideoUrl(id);
        StreamingResponseBody stream = out -> {
            try (InputStream inputStream = Files.newInputStream(videoFile.toPath())) {
                byte[] bytes = new byte[1024 * 1024 * 2]; // 2M
                int length;
                while ((length = inputStream.read(bytes)) != -1) {
                    out.write(bytes, 0, length);
                }
                System.out.println("视频下载成功");
                out.flush();
            } catch (final Exception e) {
                throw new RuntimeException(e);
            }
        };

        stream.writeTo(response.getOutputStream());
    }
;