vue2+element-ui 实现OSS分片上传+取消上传

  • 遇到问题:项目中需要上传500MB以上的视频。一开始使用上传组件el-upload,调用后台接口,但是出现了onprogress显示百分百后接口一直pending,过了很多秒后接口才通,如果遇到大文件的话,接口就会报超时。

  • 解决办法:

    使用阿里云OSS的分片上传。调用OSS时报No'Access-Control-Allow-Origin'的错误

    一定要设置跨域规则!!!否则会报No'Access-Control-Allow-Origin'的错误

1. 创建oss.js文件

js 复制代码
let OSS = require('ali-oss');

let client = new OSS({
  region: '', // 填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
  accessKeyId: '',
  accessKeySecret: '',
  bucket: '', // 填写Bucket名称
});

let cdnUrl = '' // 文件上传后的回调地址

export { client, cdnUrl };

2. 创建一个上传视频组件 VideoUpload.vue

js 复制代码
<template>
  <div class="component-upload-image">
    <el-upload action="" :http-request="beforeUpload" class="avatar-uploader" :limit="limit"
      :on-error="handleUploadError" :on-exceed="handleExceed" name="file" :show-file-list="false" :file-list="fileList"
      ref="uploadRef">
      <video v-if="videoForm.showVideoPath && !videoFlag" :src="videoForm.showVideoPath" class="avatar video-avatar"
        controls="controls">
        您的浏览器不支持视频播放
      </video>
      <!-- //i标签是上传前的那个+上传后隐藏 -->
      <i v-else-if="!videoForm.showVideoPath && !videoFlag" class="el-icon-plus avatar-uploader-icon"></i>
      <el-progress v-if="videoFlag == true" type="circle" :percentage="videoUploadPercent"
        style="margin-top: 7px"></el-progress>
    </el-upload>
    <el-button v-if="isShowBtn && videoForm.showVideoPath" class="mt-20" plain round @click="handleDelete" size="small"
      type="primary">重新上传<i class="el-icon-upload el-icon--right"></i></el-button>
  </div>
</template>

<script>
import { client, cdnUrl } from "./oss";

export default {
  props: {
    value: [String, Object, Array],
    // 图片数量限制
    limit: {
      type: Number,
      default: 1,
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 5120,
    },
    fileType: {
      type: Array,
      default: () => ["video/*"],
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true,
    },
    // 是否显示进度条
    isShowUploadVideo: {
      type: Boolean,
      default: false,
    },
    // 是否显示重新上传按钮
    isShowBtn: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      dialogImageUrl: "",
      dialogVisible: false,
      // hideUpload: false,
      // baseUrl: process.env.VUE_APP_BASE_API,
      // uploadImgUrl: process.env.VUE_APP_BASE_API + "/file/upload", // 上传的图片服务器地址
      fileList: [],
      videoForm: {
        showVideoPath: "", //回显的变量
      },
      videoFlag: false,
      videoUploadPercent: 0,
      isCancel: false
    };
  },
  watch: {
    value: {
      handler(val) {
        if (val) {
          this.videoForm.showVideoPath = val;
          // 首先将值转为数组
          const list = Array.isArray(val) ? val : this.value.split(",");
          // 然后将数组转为对象数组
          this.fileList = list.map((item) => {
            if (typeof item === "string") {
              item = { name: item, url: item };
            }
            return item;
          });
        } else {
          this.fileList = [];
          return [];
        }
      },
      deep: true,
      immediate: true,
    },
  },
  computed: {
    // 是否显示提示
    showTip() {
      return this.isShowTip && (this.fileType || this.fileSize);
    },
  },
  methods: {
    //自定义上传方法..
    Upload(file) {
      debugger;
      //判断扩展名
      const tmpcnt = file.file.name.lastIndexOf(".");
      const exname = file.file.name.substring(tmpcnt + 1);
      //  配置路径以及文件名称
      const fileName = "files/" + file.file.uid + "." + exname;
      const progress = (p, _checkpoint) => {
        this.videoFlag = true;
        this.videoUploadPercent = Number((Number(p) * 100).toFixed(1));
        console.log(this.isCancel);
        if (this.isCancel) {
          console.log("取消上传");
          client.cancel();
          this.isCancel = false;
        }
      };
      client.multipartUpload(fileName, file.file, {
        progress,
        // 设置并发上传的分片数量。
        // parallel: 4,
        // 设置分片大小。默认值为1 MB,最小值为100 KB。
        partSize: 5 * 1024 * 1024,
      }).then((res) => {
        // console.log(res, "res");
        this.videoFlag = false;
        if (res.name) {
          this.videoForm.showVideoPath = cdnUrl + res.name;
          this.$emit("input", this.videoForm.showVideoPath, this.duration);
          // this.loading.close();
        } else {
          this.$modal.msgError("上传视频失败,请重试");
          // this.loading.close();
          this.handleDelete();
        }
      })
        .catch((err) => {
          console.log(err);
          if (err.name == 'cancel') {
            this.$message('上传取消');
          } else {
            this.$modal.msgError(err);
          }
          this.handleDelete();
        });
    },
    handleDelete() {
      this.isCancel = true;
      this.videoFlag = false;
      this.$refs.uploadRef.clearFiles();
      this.duration = 0;
      this.videoForm.showVideoPath = "";
      this.$emit("input", this.videoForm.showVideoPath, this.duration); // 清除已上传的文件
    },
    // 上传前
    beforeUpload(file) {
      var fileSize = file.file.size / 1024 / 1024 < this.fileSize; //控制大小  修改50的值即可
      console.log(file.file.type);
      if (
        this.fileType.indexOf(file.file.type) == -1 //控制格式
      ) {
        this.$modal.msgError(
          `文件格式不正确, 请上传${this.fileType.join("/")}视频格式文件!`
        );
        return false;
      }
      if (!fileSize) {
        this.$modal.msgError(`上传视频大小不能超过 ${this.fileSize} MB!`);
        return false;
      }
      // 获取视频时长
      var url = URL.createObjectURL(file.file);
      var audioElement = new Audio(url);
      var time;
      var that = this;
      audioElement.addEventListener("loadedmetadata", function () {
        time = audioElement.duration; //时长为秒
        that.duration = time;
      });
      // 一开始设置的全屏遮罩层,考虑到用户可能想取消上传,于是去除
      // this.loading = this.$loading({
      //   lock: true,
      //   text: "上传中",
      //   background: "rgba(0, 0, 0, 0.7)",
      // });
      this.Upload(file);
    },
    // 文件个数超出
    handleExceed() {
      this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
    },
    // 上传失败
    handleUploadError() {
      this.$modal.msgError("上传失败,请重试");
      // this.loading.close();
    },
  },
};
</script>
<style scoped lang="scss">
// .el-upload--picture-card 控制加号部分
::v-deep.hideUpload .el-upload--picture-card {
  display: none;
}

::v-deep .el-upload--picture-card {
  width: 104px;
  height: 104px;
  line-height: 104px;
}

::v-deep .el-upload-list--picture-card .el-upload-list__item {
  width: 104px;
  height: 104px;
}

.avatar-uploader-icon {
  border: 1px dashed #d9d9d9 !important;
}

.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9 !important;
  border-radius: 6px !important;
  position: relative !important;
  overflow: hidden !important;
}

.avatar-uploader .el-upload:hover {
  border: 1px dashed #d9d9d9 !important;
  border-color: #409eff;
}

.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 300px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}

.avatar {
  width: 300px;
  height: 178px;
  display: block;
}
</style>

在父组件中调用

js 复制代码
<template>
  <div class="app-container">
    <el-dialog title="" :visible.sync="open" append-to-body @close="cancel">
    	<VideoUpload v-model="formClass.ossUrl" :limit="1" :fileType="fileType" :fileSize="5120" :isShowUploadVideo="true" @input="uploadVideo" ref="videoUploadRef" :isShowBtn="true"></VideoUpload>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      open: false,
      formClass: {
        ossUrl: "", //阿里云oss地址-视频
      },
      fileType: ["video/mp4"],
    };
  },
  methods: {
    submitForm() {},
    cancel() {
      if (this.$refs.videoUploadRef) {
        this.$refs.videoUploadRef.handleDelete();
      }
      this.open = false;
    },
  },
};
</script>

原文链接:https://blog.csdn.net/wcy0112/article/details/136843100

相关推荐
沐土Arvin14 分钟前
深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器
开发语言·前端·javascript·设计模式·html
专注VB编程开发20年15 分钟前
VB.NET关于接口实现与简化设计的分析,封装其他类
java·前端·数据库
小妖66625 分钟前
css 中 content: “\e6d0“ 怎么变成图标的?
前端·css
L耀早睡1 小时前
mapreduce打包运行
大数据·前端·spark·mapreduce
HouGISer1 小时前
副业小程序YUERGS,从开发到变现
前端·小程序
outstanding木槿1 小时前
react中安装依赖时的问题 【集合】
前端·javascript·react.js·node.js
霸王蟹2 小时前
React中useState中更新是同步的还是异步的?
前端·javascript·笔记·学习·react.js·前端框架
霸王蟹2 小时前
React Hooks 必须在组件最顶层调用的原因解析
前端·javascript·笔记·学习·react.js
专注VB编程开发20年2 小时前
asp.net IHttpHandler 对分块传输编码的支持,IIs web服务器后端技术
服务器·前端·asp.net
爱分享的程序员3 小时前
全栈项目搭建指南:Nuxt.js + Node.js + MongoDB
前端