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

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

  • 解决办法:
    使用阿里云OSS的分片上传。调用OSS时报No’Access-Control-Allow-Origin’的错误
    一定要设置跨域规则!!!否则会报No’Access-Control-Allow-Origin’的错误

image.png

1. 创建oss.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

<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>


在父组件中调用

<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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1574107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

批量复制空白文件夹,轻松管理文件,高效办公新选择

在繁忙的办公环境中&#xff0c;文件管理是提升工作效率的关键。想象一下&#xff0c;你需要为每一个新项目、新任务或新客户创建一个新的文件夹来整理和存储相关文件。手动一个一个地创建文件夹不仅耗时&#xff0c;而且容易出错。现在&#xff0c;我们为你带来一个全新的解决…

R语言中的常用数据结构

目录 R对象的基本类型 R对象的属性 R的数据结构 向量 矩阵 数组 列表 因子 缺失值NA 数据框 R的数据结构总结 R语言可以进行探索性数据分析&#xff0c;统计推断&#xff0c;回归分析&#xff0c;机器学习&#xff0c;数据产品开发 R对象的基本类型 R语言对象有五…

[leetcode] 25. K 个一组翻转链表

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

老板们注意了,AI可能在悄悄威胁你的工作

前天,科技新闻大佬The Register发了一篇文章,说的是AI在科研领域的管理角色越来越大,可能会让管理岗位变得过时,听起来是不是有点儿疯狂? ESMT Berlin的研究小伙伴们发现,AI能够以更大的规模和效率来管理研究项目,比如审查科学文献和预测创新化合物等等,而不是取代人类…

【Frida】【Android】 工具篇:ProxyPin抓包详解

&#x1f6eb; 系列文章导航 【Frida】【Android】01_手把手教你环境搭建 https://blog.csdn.net/kinghzking/article/details/136986950【Frida】【Android】02_JAVA层HOOK https://blog.csdn.net/kinghzking/article/details/137008446【Frida】【Android】03_RPC https://bl…

【JVM】JVM简介

文章目录 &#x1f334;简介&#x1f332;JVM发展史&#x1f338;Sun Classic VM&#x1f338;Exact VM&#x1f338;HotSpot VM&#x1f338;JRockit&#x1f338;J9 JVMTaobao JVM&#xff08;国产研发&#xff09; &#x1f333;JVM 运行流程⭕总结 &#x1f334;简介 JVM …

【项目启动执行指定代码】⭐️通过案例测试下常用的实现方式

目录 前言 1、监听 ApplicationContext事件 2、实现CommandLineRunner接口 3、实现ApplicationRunner接口 4、使用PostConstruct注解 章末 前言 为了保证程序在启动后的稳定性&#xff0c;需要执行初始化操作&#xff0c;像加载配置&#xff0c;建立数据库连接可以在项目启…

(2024)Ubuntu源码安装多个版本的opencv并切换使用

本人工作会用到x86_64的opencv和aarch64的opencv&#xff0c;所以写下来备忘自用 一、源码编译安装 依赖库安装&#xff1a; sudo apt-get install build-essential libgtk2.0-dev libgtk-3-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev o…

简单介绍css及其代码样式

css简介 css用于前端开发&#xff0c;负责对界面进行美化。让页面更美观。 他可以改变html代码的样式&#xff0c;让html代码的网页不那么死板。 css代码格式 选择器 {属性:值; 属性:值&#xff1b;} css的模版架构 css代码放到<style>标签中。 而<style>通常是…

在单交换机局域网中,不同网段的主机通信探秘

在理解局域网中不同网段主机之间的通信之前&#xff0c;我们首先要明白网络的基本组成和工作原理。局域网&#xff08;LAN&#xff09;是一个封闭的网络环境&#xff0c;通常由交换机&#xff08;Switch&#xff09;作为核心设备连接网络中的各个主机。当我们谈论不同网段的主机…

【C#】yield使用

&#x1f4bb;代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks;namespace ConsoleApp15 {internal class Program{static void Main(string[] args){IEnumerable<int&…

如何回答孩子“为什么要过清明?”

清明&#xff0c;是中国人寻根问祖的时节。 为什么要过清明节&#xff1f; 不知道你是否认真想过&#xff0c;我们为什么要过清明节呢&#xff1f; 如果当孩子问起时&#xff0c;你又该如何作答呢&#xff1f; 也许&#xff0c;以下的总结可供你参考。 清明&#xff0c;让…

[蓝桥杯 2019 国 C] 数正方形

[蓝桥杯 2019 国 C] 数正方形 题目描述 在一个 N N N \times N NN 的点阵上&#xff0c;取其中 4 4 4 个点恰好组成一个正方形的 4 4 4 个顶点&#xff0c;一共有多少种不同的取法&#xff1f; 由于结果可能非常大&#xff0c;你只需要输出模 1 0 9 7 10^9 7 1097 的…

DasViewer中,像下图山坡是选择拟合平面还是自定义平面?还是其他的基准面?

问题如图 如若山坡是计算斜面的土方&#xff0c;可以选择用拟合平面模式&#xff0c;该模式适用斜坡。 DasViewer是由大势智慧自主研发的免费的实景三维模型浏览器,采用多细节层次模型逐步自适应加载技术,让用户在极低的电脑配置下,也能流畅的加载较大规模实景三维模型,提供方…

广州到菲律宾海运双清到门,菲律宾双清到门一站式物流

洗衣液出口菲律宾全程海运双清包税到门物流操作流程 为了便于中国的洗衣液产品出口至菲律宾&#xff0c;并实现海运双清包税到门的服务&#xff0c;我们提供一站式的中菲物流解决方案。我们的目标是在保证货物安全的前提下&#xff0c;为您完成从中国起运到菲律宾指定地址的所有…

Java基于微信小程序高校体育场管理小程序

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表

RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表 文章目录 RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表1. MQTT概览2. MQTT 5.0 特性1. 特性概要2. Docker中安装RabbitMQ及启用MQTT5.0协议 3. MQTT 5.0 功能列表1. 消息过期1. 描述2. 举例3. 实现 2. 订阅标识…

含风电-光伏-光热电站电力系统N-k安全优化调度模型

目录 1 主要内容 2 部分程序 3 部分结果 4 下载链接 1 主要内容 该程序参考《光热电站促进风电消纳的电力系统优化调度》光热电站模型&#xff0c;主要做的是考虑N-k安全约束的含义风电-光伏-光热电站的电力系统优化调度模型&#xff0c;从而体现光热电站在调度灵活性以及经…

Linux 常用指令及其理论知识

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;http://t.csdnimg.cn/Tvyou 欢迎各位指教&#xff01;&#xff01;&#xff01; 目录 一、理论知识 二、基础指令 1、ls指令&#xff08;列出该目录下的所有子目录和文件&#xff09; 语法&#xff1a; …

扫描电镜如何能拍到样品的好的形貌?

扫描电镜是表征材料微观形貌的有力工具&#xff0c;它能够呈现样品的精细结构。然而&#xff0c;要拍摄出高质量的样品形貌并非易事&#xff0c;除了要熟悉扫描电镜的各种功能&#xff0c;还需要掌握一些技巧。本文将介绍如何利用景深、倾斜校正、动态聚焦等功能以及合轴和消像…