当前位置: 首页 > news >正文

【VUE】前端阿里云OSS断点续传,分片上传

什么是OSS:

数据以对象(Object)的形式存储在OSS的存储空间(Bucket )中。如果要使用OSS存储数据,您需要先创建Bucket,并指定Bucket的地域、访问权限、存储类型等属性。创建Bucket后,您可以将数据以Object的形式上传到Bucket,并指定Object的文件名(Key)作为其唯一标识。

分片上传:

在上传大文件(超过5 GB)到OSS的过程中,如果出现网络中断、程序异常退出等问题导致文件上传失败,您需要使用分片上传的方式上传大文件。分片上传通过将待上传的大文件分成多个较小的碎片(Part),充分利用网络带宽和服务器资源并发上传多个Part,加快上传完成时间,并在Part上传完成之后调用CompleteMultipartUpload接口将这些Part组合成一个完整的Object。

断点续传上传:

在上传大文件(超过5 GB)到OSS的过程中,如果出现网络中断、程序异常退出等问题导致文件上传失败,甚至重试多次仍无法完成上传,您需要使用断点续传上传的方式。断点续传上传将需要上传的大文件分成多个较小的分片并发上传,加速上传完成时间。如果上传过程中,某一分片上传失败,再次上传时会从Checkpoint文件记录的断点继续上传,无需重新上传所有分片。上传完成后,所有分片将合并成完整的文件。

 一.验证服务端是否已有该文件、该文件是否已上传部分块?

点击上传文件→选择文件后→读取文件信息→计算MD5值→发送给服务器→查看服务器返回信息

 计算MD5:

import SparkMD5 from 'spark-md5';this.calculateMD5(files.raw, this.sliceSize).then(async (md5) => {console.log(md5)}).catch((error) => {this.$message.error(error);});// md5 计算calculateMD5(file, chunkSize) {return new Promise((resolve, reject) => {const chunks = Math.ceil(file.size / chunkSize);let currentChunk = 0;const spark = new SparkMD5.ArrayBuffer();const fileReader = new FileReader();fileReader.onload = (e) => {spark.append(e.target.result); // 更新 MD5 值currentChunk++;if (currentChunk < chunks) {this.loadNextChunk(fileReader, currentChunk, chunkSize, file);} else {const md5 = spark.end(); // 计算最终 MD5 值resolve(md5);}};fileReader.onerror = function (e) {reject(e);};this.loadNextChunk(fileReader, currentChunk, chunkSize, file);});},// md5 计算loadNextChunk(fileReader, currentChunk, chunkSize, file) {const start = currentChunk * chunkSize;const end = Math.min(start + chunkSize, file.size);const chunk = file.slice(start, end);fileReader.readAsArrayBuffer(chunk);},

二 文件切块

			sliceSize: 1024 * 1024 * 2, // 分片上传-每片5MBfileBlockArr: [],_this.fileBlockArr = [];let blob = file.raw;const { size: fileSize, name: fileName } = blob;//计算文件切片总数,Math.ceil向上取整const totalSlice = Math.ceil(fileSize / _this.sliceSize);_this.chunktotle = totalSlice;for (let i = 0; i < totalSlice; i++) {let start = i * _this.sliceSize;let end = Math.min(fileSize, start + _this.sliceSize);let chunkBlob = blob.slice(start, end); // 直接使用slice方法分片Blob对象_this.fileBlockArr.push(chunkBlob);}

三 调用初始化接口

调用初始化接口,拿到部分参数后进行块上传

						const requestList = [];_this.fileBlockArr.forEach((item, index) => {if (index + 1 >= sliceNo) {const fn = () => {let data = new FormData();data.append('file', item);data.append('fileSlicesNum', totalSlice);data.append('sliceNo', index + 1);data.append('fileMD5', md5);data.append('resourceKey', _this.resourceKey);data.append('ossSlicesId', _this.ossSlicesId);data.append('fileSize', fileSize);return axios({url: process.env.VUE_APP_BASE_API + '/common/partUpload',method: 'post',data: data,headers: {Authorization: 'Bearer ' + getToken(),'Content-Type': 'multipart/form-data',},}).then((res) => {if (res.data.code === 200) {if (_this.percentCount === 0) {// 避免上传成功后会删除切片改变 chunkList 的长度影响到 percentCount 的值_this.percentCount = 100 / _this.fileBlockArr.length;}// _this.percent += _this.percentCount; // 改变进度_this.percent = res.data.progressPercent;_this.fileBlockArr.splice(index, 1); // 一旦上传成功就删除这一个 chunk,方便断点续传}}).catch((err) => {return Promise.reject(err);});};requestList.push(fn);}});let i = 0; // 记录发送的请求个数

四 上传与合并

						let i = 0; // 记录发送的请求个数const complete = () => {let completedData = {ossSlicesId: _this.ossSlicesId,resourceKey: _this.resourceKey,fileMD5: _this.md5,};completePart(completedData).then((response) => {if (response.code == 200) {_this.form.versionUrl = response.data;}}).catch((error) => {console.log('completePart-error:', error);});};const send = async () => {if (i >= requestList.length) {complete();return;}await requestList[i]();i++;send();};send(); // 发送请求

全览

data(){return {sliceSize: 1024 * 1024 * 2, // 分片上传-每片5MBchunktotle: 0,percentCount: 0,percent: 0,md5: '',fileBlockArr: [],resourceKey: undefined,ossSlicesId: undefined,fileName: undefined,}},methods:{uploadChange(files, fileList) {this.fileName = files.name;this.calculateMD5(files.raw, this.sliceSize).then(async (md5) => {this.md5 = md5;// 分片验证let checkData = {fileMD5: this.md5,};continueUpload(checkData).then((response) => {if (response.code === 200) {if (response.data !== null &&response.data.sliceNo < response.data.fileSlicesNum) {//已存在,但未上传完成 取已上传到的块信息this.resourceKey = response.data.resourceKey;this.ossSlicesId = response.data.ossSlicesId;let sliceNo = response.data.sliceNo;blockUpload(files, sliceNo + 1);} else if (response.data !== null &&response.data.sliceNo == response.data.fileSlicesNum) {// 上传完成待合并let completedData = {ossSlicesId: response.data.ossSlicesId,resourceKey: response.data.resourceKey,fileMD5: _this.md5,};mergePart(completedData);} else {// 未上传partInit({fileName: files.name,storageDirectory: 'internalApp',}).then((response) => {if (response.code === 200) {this.resourceKey = response.data.resourceKey;this.ossSlicesId = response.data.ossSlicesId;blockUpload(files, 0);}});}}}).catch((error) => {console.log('continueUpload-error:', error);});function mergePart(completedData) {completePart(completedData).then((response) => {if (response.code == 200) {let fileData = {name: _this.fileName,url: response.data,};_this.fileList.push(fileData);_this.form.versionUrl = response.data;}}).catch((error) => {console.log('completePart-error:', error);});}let _this = this;function blockUpload(file, sliceNo) {_this.fileBlockArr = [];let blob = file.raw;const { size: fileSize, name: fileName } = blob;//计算文件切片总数,Math.ceil向上取整const totalSlice = Math.ceil(fileSize / _this.sliceSize);_this.chunktotle = totalSlice;for (let i = 0; i < totalSlice; i++) {let start = i * _this.sliceSize;let end = Math.min(fileSize, start + _this.sliceSize);let chunkBlob = blob.slice(start, end); // 直接使用slice方法分片Blob对象_this.fileBlockArr.push(chunkBlob);}const requestList = [];_this.fileBlockArr.forEach((item, index) => {if (index + 1 >= sliceNo) {const fn = () => {let data = new FormData();data.append('file', item);data.append('fileSlicesNum', totalSlice);data.append('sliceNo', index + 1);data.append('fileMD5', md5);data.append('resourceKey', _this.resourceKey);data.append('ossSlicesId', _this.ossSlicesId);data.append('fileSize', fileSize);return axios({url: process.env.VUE_APP_BASE_API + '/common/partUpload',method: 'post',data: data,headers: {Authorization: 'Bearer ' + getToken(),'Content-Type': 'multipart/form-data',},}).then((res) => {if (res.data.code === 200) {if (_this.percentCount === 0) {// 避免上传成功后会删除切片改变 chunkList 的长度影响到 percentCount 的值_this.percentCount = 100 / _this.fileBlockArr.length;}// _this.percent += _this.percentCount; // 改变进度_this.percent = res.data.progressPercent;_this.fileBlockArr.splice(index, 1); // 一旦上传成功就删除这一个 chunk,方便断点续传}}).catch((err) => {return Promise.reject(err);});};requestList.push(fn);}});let i = 0; // 记录发送的请求个数const complete = () => {let completedData = {ossSlicesId: _this.ossSlicesId,resourceKey: _this.resourceKey,fileMD5: _this.md5,};mergePart(completedData);};const send = async () => {if (i >= requestList.length) {complete();return;}await requestList[i]();i++;send();};send(); // 发送请求}}).catch((error) => {this.$message.error(error);});},// md5 计算calculateMD5(file, chunkSize) {return new Promise((resolve, reject) => {const chunks = Math.ceil(file.size / chunkSize);let currentChunk = 0;const spark = new SparkMD5.ArrayBuffer();const fileReader = new FileReader();fileReader.onload = (e) => {spark.append(e.target.result); // 更新 MD5 值currentChunk++;if (currentChunk < chunks) {this.loadNextChunk(fileReader, currentChunk, chunkSize, file);} else {const md5 = spark.end(); // 计算最终 MD5 值resolve(md5);}};fileReader.onerror = function (e) {reject(e);};this.loadNextChunk(fileReader, currentChunk, chunkSize, file);});},// md5 计算loadNextChunk(fileReader, currentChunk, chunkSize, file) {const start = currentChunk * chunkSize;const end = Math.min(start + chunkSize, file.size);const chunk = file.slice(start, end);fileReader.readAsArrayBuffer(chunk);},
}

相关文章:

【VUE】前端阿里云OSS断点续传,分片上传

什么是OSS&#xff1a; 数据以对象&#xff08;Object&#xff09;的形式存储在OSS的存储空间&#xff08;Bucket &#xff09;中。如果要使用OSS存储数据&#xff0c;您需要先创建Bucket&#xff0c;并指定Bucket的地域、访问权限、存储类型等属性。创建Bucket后&#xff0c;您…...

春招面试高频题目总结

面试问题 redis 可以用于进程间通信吗&#xff1f; Why&#xff1f;How? ---> 延展一下 有哪些进程间通信技术, 优劣如何&#xff1f; 有大量的插入sql语句&#xff0c;一条条的插入性能很差&#xff0c;如何通过事务进行优化&#xff1f; 保证线程安全的策略有哪些&…...

基于SSM+Jsp+Mysql的KTV点歌系统

基于SSMJspMysql的KTV点歌系统 基于SSMJspMysql的KTV点歌系统的设计与实现 开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工…...

Docker Oracle提示密码过期

进入docker docker exec -it oracle bash 修改环境变量文件 vi .bash_profile 为以下内容 # .bash_profile# Get the aliases and functions if [ -f ~/.bashrc ]; then. ~/.bashrc fi# User specific environment and startup programsPATH$PATH:$HOME/binexport PATH expo…...

5.3、【AI技术新纪元:Spring AI解码】图像生成API

Spring 图像生成API Spring图像生成API旨在提供一个简单且便携的接口,用于与各类专注于图像生成的AI模型交互,使开发者能够在不同图像相关模型之间轻松切换,只需对代码进行最少的改动。这一设计遵循了Spring框架的模块化和可互换性理念,确保开发人员能够快速调整其应用程序…...

自营、入驻商城小程序开发

IT外包的隐形重负——沟通成本&#xff1a;当客户仅能提供大致需求&#xff0c;而IT公司则机械地执行&#xff0c;往往会导致项目细节中充满漏洞&#xff0c;用户体验大打折扣。 最终&#xff0c;项目不断延期&#xff0c;进度一拖再拖。探究其根源&#xff0c;客户往往并非IT…...

C++关键字:const

文章目录 一、const的四大作用1.修饰 变量、数组2.修饰 函数的形参、修饰 引用 (最常用&#xff09;3.修饰 指针&#xff1a;常量指针、指针常量 、只读指针4.修饰 类的成员函数、修饰 类的对象 一、const的四大作用 1.修饰 变量、数组 1.const修饰变量&#xff1a; 被const修…...

nodejs 常用命令

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;常用于服务器端编程。以下是一些 Node.js 的常用命令 1、安装 Node.js&#xff1a; 通常&#xff0c;你会从 Node.js 的官方网站下载并安装适合你操作系统的版本。安装完成后&#xff0c;你可以在命令行中…...

nginx配置详解+nginx_lua模块的使用

nginx基本配置详解 目录 nginx基本配置详解 nginx_lua模块使用方式 openresty介绍与安装 lua基本语法使用 全局配置&#xff1a; user&#xff1a;指定Nginx主进程运行的用户。在下方示例中&#xff0c;Nginx将以root用户身份运行。worker_processes&#xff1a;指定Ngi…...

大数据--hdfs--java编程

环境&#xff1a; virtualbox ubantu1604 Linux idea社区版2023 jdk1.8 hadoop相关依赖 使用java操作 1. 判断/user/stu/input/test.txt文件是否存在&#xff0c;存在则读出文件内容&#xff0c;打印在控制台上。反之&#xff0c;输出“文件不存在”。 package abc;impo…...

力扣由浅至深 每日一题.10 最后一个单词的长度

日子都是崭新的&#xff0c;我们下一章见 ——24.3.21 最后一个单词的长度 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后…...

21 OpenCV 直方图均衡化

文章目录 直方图概念均衡的目的equalizeHist 均衡化算子示例 直方图概念 图像直方图&#xff0c;是指对整个图像像在灰度范围内的像素值(0~255)统计出现频率次数&#xff0c;据此生成的直方图&#xff0c;称为图像直方图-直方图。直方图反映了图像灰度的分布情况。 均衡的目的…...

对七层代理、四层代理、正向代理、反向代理的认识

一、理解nginx服务代理 Nginx代理有正向和反向代理两种类型&#xff0c;可以基于osi七层模型中的第四层&#xff08;传输层&#xff09;和第七层&#xff08;应用层&#xff09;进行代理 注&#xff1a; nginx 一般支持的是7层代理&#xff0c;支持四层代理一般使用 lvs 或者ha…...

网络: 五种IO模型

阻塞IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式 非阻塞IO:若内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码. 信号驱动: 内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作 多路转接: 能够同时等…...

AI大模型学习在当前技术环境下的重要性与发展前景

目录 前言1 学科基础与技能要求1.1 数学基础的深厚性1.2 编程能力的必要性1.3 对特定领域业务场景的了解 2 模型结构与算法的优化2.1 模型结构的不断演进2.2 算法优化的重要性2.3 准确性与效率的提升 3 AI大模型学习的应用场景3.1 自然语言处理3.2 计算机视觉3.3 推荐系统 结语…...

【呼市经开区建设服务项目水、电能耗监测 数采案例】

实施方案 针对能耗采集中的水、电能源数据采集&#xff0c;因客观因素条件&#xff0c;数据采集方面存在较大难度。大多数国网电表485接口由于封签限制&#xff0c;不能实施采集&#xff0c;不让拆机接线&#xff0c;采集实施存在困难。水量能耗采集&#xff0c;存在类似问题&a…...

深度学习算法工程师面试常见问题及解答

基础知识和理论&#xff1a; 解释什么是深度学习以及它与机器学习的关系。描述神经网络的基本结构和工作原理。什么是反向传播算法&#xff0c;它是如何工作的&#xff1f;激活函数的作用是什么&#xff1f;常见的激活函数有哪些&#xff1f;什么是损失函数&#xff1f;常见的损…...

OKR与敏捷开发、精益创业等方法如何协同工作?

在快速变化的市场环境中&#xff0c;企业需要更加灵活和高效地应对各种挑战。目标与关键成果法&#xff08;OKR&#xff09;、敏捷开发以及精益创业等方法&#xff0c;作为现代企业管理的重要工具&#xff0c;各自在推动企业发展、提高团队效率、优化产品迭代等方面发挥着不可或…...

【ESP32 Arduino】定时器的使用

文章目录 前言一、ESPTIMER定时器的介绍1.1 定时器是什么 二、分频系数2.1 为什么需要分频系数2.2 分频系数怎么计算 三、定时器的使用3.1 初始化定时器对象3.2 设置中断时间3.3 设置回调函数3.4 使能定时器 四、示例代码总结 前言 在嵌入式系统中&#xff0c;定时器是一项重要…...

网络基础(一)初识

1、计算机网络背景 1.1、网络发展 1. 独立模式: 计算机之间相互独立&#xff1b; 2. 网络互联: 多台计算机连接在一起&#xff0c;完成数据共享&#xff1b; 3. 局域网LAN: 计算机数量更多了, 通过交换机和路由器连接在一起; 4. 广域网WAN: 将远隔千里的计算机都连在一起;…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...