eggjs 实现视频流上传

eggjs 实现视频流上传_第1张图片

前言

最近公司搞了个大屏,结果客户上传了一个400M的视频,由于网速慢,直接在上传途中给超时了,然后后端用 JAVA 写的个流上传,我想想那我自己也搞个 流上传 , 然后就诞生了这个最最最基础的版本,其他改动后续更新

目录

  1. egg.js 配合 jwt 进行鉴权
  2. egg.js 配置 mysql 以及简单登录实现
  3. eggjs+mysql实现图片上传
  4. eggjs 实现增删改查
  5. eggjs 搭配 vue+axios 实现登录和获取用户信息
  6. eggjs 实现流上传

开发

后端

本章节主要讲解下eggjs怎么通过stream写入视频

修改 app => config => config.default.js

 // 除了upload接口用 mode:'file' , 其他都用 `stream`
  config.multipart = {
    mode: 'stream',
    fileModeMatch: /(\/upload)$/,
    fileSize: 1048576000,
    whitelist: [".txt", ".png", ".jpg",'.mp4'],
  };

修改 app => controller => upload.js 加入

  async uploadStream() {
    const stream = await this.ctx.getFileStream();
    const {
      filename,
      fields: { chunk, chunks },
    } = stream;
    const day = moment(new Date()).format("YYYYMMDD");
    const dir = path.join(this.config.uploadDir, day);
    let buf;
    let uploadDir = "";
    try {
      await mkdirp(dir); // 不存在就创建目录
      uploadDir = path.join(dir, filename);
      const streams = fs.createWriteStream(uploadDir, {
        flags: "a",
      });
      const parts = await toArray(stream);
      buf = Buffer.concat(parts);
      streams.write(buf);
      // 当 chunk === chunks , 证明已经文件已经上传完毕 , 可以直接写入数据库
      if (chunk === chunks) {
        console.log("上传数据库");
      }
    } catch (e) {
      await sendToWormhole(stream);
      ctx.cleanupRequestFiles();
      throw e;
    }
    this.ctx.body = {
      code: 200,
      msg: "上传成功",
      data: uploadDir.replace(/app/g, ""),
    };
  }
}

修改 app => router.js , 加入

 router.post('/uploadStream',_jwt, controller.upload.uploadStream);

接下来我们通过ajax写一个接口请求

前端

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="file" id="file" />

    <button id="upload">上传</button>

    <script src="../js/jquery.js"></script>
    <script>
      $('#upload').on('click', function (params) {
        let file = $('#file').prop('files')[0];
        let uid = new Date().getTime();
        let sliceSize = 10 * 1024 * 1024;
        let blob = file;
        const fileSize = file.size; // 文件大小
        const fileName = file.name; // 文件名
        const totalSlice = Math.ceil(fileSize / sliceSize);
        const pormiseArr = [];
        for (let i = 1; i <= totalSlice; i++) {
          let chunk;
          if (i == totalSlice) {
            chunk = file.slice((i - 1) * sliceSize, fileSize); //切割文件
          } else {
            chunk = file.slice((i - 1) * sliceSize, i * sliceSize);
          }
          const formData = new FormData();
          formData.append('size', fileSize);
          formData.append('chunks', totalSlice);
          formData.append('chunk', i);
          formData.append('name', uid + fileName);
          formData.append('file', chunk, uid + fileName);
          pormiseArr.push(formData);
        }
        callback(pormiseArr, 0);
      });
      // 这里之所以采用回调,是因为如果全部通过同步的,可能会出现后端拼接报错的可能
      // 这里回调其实并没有解决上传较慢的问题,但是在一定层度解决了上传太久出现超时的问题
      function callback(arr, index) {
        $.ajax({
          url: 'http://localhost:7001/uploadStream',
          type: 'POST',
          data: arr[index],
          processData: false, // 不处理数据
          contentType: false, // 不设置内容类型
          success: function () {
            if (index != arr.length - 1) {
              callback(arr, index + 1);
            } else {
              console.log('上传成功');
            }
          },
        });
      }
    </script>
  </body>
</html>

结果

eggjs 实现视频流上传_第2张图片

改进

  1. 如上诉说的,因为服务器并没有通过多线程等上传,导致服务器在前端进行上传时可能导致服务器拼接流的时候出现错乱…,这个问题我是用前端解决,通过一个上传后再上传另一个来解决这个问题,但是这个问题并不能缓解上传慢的问题(上传快可以,加宽带价钱==),但是解决了上传后超时的问题
  2. 我们通过 chunkchunks 可以判断上传进度,所以 (1)前端可以写个进度条方便用户更直观看到上传进度(2)后端可以通过这两个字段来实现断点续传的功能,也就是不断通过记录 chunk 来记录进度的 位置 (tips: 这可能是个不太成熟的想法)
  3. 播放流的问题最近也在 着手 ,其实 video标签就可以实现了,如果有 token 需求可以用 xgplayer,也不失为一种解决方法

你可能感兴趣的:(vue.js,javascript,前端)