Vue Bootstrap OSS 实现文件追加上传、断点续传、极速秒传

公司实现文件上传技术选型采用后端SpringBoot/Cloud,前端vue Bootstrap ,阿里云OSS作为文件存储,大文件上传功能单独抽取封装大文件上传组件,可供所有的大文件的操作。

后端框架版本
SpringBoot2.5.6
Spring-Cloud2020.0.4
mysql8.0.26
pagehelper1.3.1
Mybatis2.2.0
Redis5.0
Fastjson1.2.78
前端框架版本
Vue2.6.11
axios0.24.0
vue-router3.5.3
Bootstrap4.6.2

文章目录

          • 一、前端部分
            • 1. 小节页面
            • 2. js部分
            • 3. 大文件上传组件
          • 二、阿里云OSS
            • 2.1. 注册阿里云
            • 2.2. 开通OSS
            • 2.3. 进入管控台
            • 2.4. 创建 Bucket
            • 2.5. 创建OSS用户
            • 2.6. OSS权限
          • 三、OSS Client 开发文档
            • 3.1. OSS Client SDK
            • 3.2. 限制
            • 3.3. SDK Client
          • 四、后端部分
            • 4.1.依赖引入
            • 4.2. 配置
            • 4.3. api接口

一、前端部分
1. 小节页面

小节页面作为文件上传父页面
在这里插入图片描述

      <div class="form-group"><label class="col-sm-2 control-label">视频</label><div class="col-sm-10"><vod :text="'上传视频'":input-id="'video-upload'":suffixs="['mp4']":use="FILE_USE.COURSE.key":after-upload="afterUpload"></vod><div v-show="section.video" class="row"><div class="col-md-9"><player v-bind:player-id="'form-player-div'"ref="player"></player><video v-bind:src="section.video" id="video" controls="controls" class="hidden"></video></div></div></div></div>
2. js部分
<script>import BigFile from "@/components/big-file";export default {components: { BigFile },name: 'business-section',data: function () {return {section: {},sections: [],FILE_USE: FILE_USE,}},methods: {/*** 点击【新增】*/add() {let _this = this_this.section = {}$("#form-modal").modal("show")},/*** 点击【编辑】*/edit(section) {let _this = this_this.section = $.extend({}, section)$("#form-modal").modal("show")},/*** 点击【保存】*/save() {let _this = this_this.section.video = "";// 保存校验if (1 != 1|| !Validator.require(_this.section.title, "标题")|| !Validator.length(_this.section.title, "标题", 1, 50)|| !Validator.length(_this.section.video, "视频", 1, 200)) {return;}_this.section.courseId = _this.course.id_this.section.chapterId = _this.chapter.idLoading.show()_this.$api.post(process.env.VUE_APP_SERVER + '/business/admin/section/save', _this.section).then((res) => {Loading.hide()let resp = res.dataif (resp.success) {$("#form-modal").modal("hide")_this.list(1)Toast.success("保存成功!")} else {Toast.warning(resp.message)}})},afterUpload(resp) {let _this = thislet video = resp.content.path;},},
}</script>
3. 大文件上传组件
<template><div><button type="button" v-on:click="selectFile()" class="btn btn-white btn-default btn-round"><i class="ace-icon fa fa-upload"></i>{{ text }}</button><input class="hidden" type="file" ref="file" v-on:change="uploadFile()" v-bind:id="inputId+'-input'"></div>
</template><script>
export default {name: 'big-file',props: {text: {default: "上传大文件"},inputId: {default: "file-upload"},suffixs: {default: []},use: {default: ""},shardSize: {default: 50 * 1024},url: {default: "oss-append"},saveType: {default: "oss/"},afterUpload: {type: Function,default: null},},data: function () {return {}},methods: {uploadFile() {let _this = this;let formData = new window.FormData();let file = _this.$refs.file.files[0];console.log(JSON.stringify(file));/*name: "test.mp4"lastModified: 1901173357457lastModifiedDate: Tue May 27 2099 14:49:17 GMT+0800 (中国标准时间) {}webkitRelativePath: ""size: 37415970type: "video/mp4"*/// 生成文件标识,标识多次上传的是不是同一个文件let key = hex_md5(file.name + file.size + file.type);let key10 = parseInt(key, 16);let key62 = Tool._10to62(key10);console.log(key, key10, key62);console.log(hex_md5(Array()));/*d41d8cd98f00b204e9800998ecf8427e2.8194976848941264e+386sfSqfOwzmik4A4icMYuUe*/// 判断文件格式let suffixs = _this.suffixs;let fileName = file.name;let suffix = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length).toLowerCase();let validateSuffix = false;for (let i = 0; i < suffixs.length; i++) {if (suffixs[i].toLowerCase() === suffix) {validateSuffix = true;break;}}if (!validateSuffix) {Toast.warning("文件格式不正确!只支持上传:" + suffixs.join(","));$("#" + _this.inputId + "-input").val("");return;}// 文件分片// let shardSize = 10 * 1024 * 1024;    //以10MB为一个分片// let shardSize = 50 * 1024;    //以50KB为一个分片let shardSize = _this.shardSize;let shardIndex = 1;		//分片索引,1表示第1个分片let size = file.size;let shardTotal = Math.ceil(size / shardSize); //总片数let param = {'shardIndex': shardIndex,'shardSize': shardSize,'shardTotal': shardTotal,'use': _this.use,'name': file.name,'suffix': suffix,'size': file.size,'key': key62};_this.check(param);},/*** 检查文件状态,是否已上传过?传到第几个分片?*/check(param) {let _this = this;_this.$api.get(process.env.VUE_APP_SERVER + '/file/admin/check/' + _this.saveType + param.key).then((response) => {let resp = response.data;if (resp.success) {let obj = resp.content;if (!obj) {param.shardIndex = 1;console.log("没有找到文件记录,从分片1开始上传");_this.upload(param);} else if (obj.shardIndex === obj.shardTotal) {// 已上传分片 = 分片总数,说明已全部上传完,不需要再上传Toast.success("文件极速秒传成功!");_this.afterUpload(resp);$("#" + _this.inputId + "-input").val("");} else {param.shardIndex = obj.shardIndex + 1;console.log("找到文件记录,从分片" + param.shardIndex + "开始上传");_this.upload(param);}} else {Toast.warning("文件上传失败");$("#" + _this.inputId + "-input").val("");}})},/*** 将分片数据转成base64进行上传*/upload(param) {let _this = this;let shardIndex = param.shardIndex;let shardTotal = param.shardTotal;let shardSize = param.shardSize;let fileShard = _this.getFileShard(shardIndex, shardSize);// 将图片转为base64进行传输let fileReader = new FileReader();Progress.show(parseInt((shardIndex - 1) * 100 / shardTotal));fileReader.onload = function (e) {let base64 = e.target.result;// console.log("base64:", base64);param.shard = base64;_this.$api.post(process.env.VUE_APP_SERVER + '/file/admin/' + _this.url, param).then((response) => {let resp = response.data;console.log("上传文件成功:", resp);Progress.show(parseInt(shardIndex * 100 / shardTotal));if (shardIndex < shardTotal) {// 上传下一个分片param.shardIndex = param.shardIndex + 1;_this.upload(param);} else {Progress.hide();_this.afterUpload(resp);$("#" + _this.inputId + "-input").val("");}});};fileReader.readAsDataURL(fileShard);},getFileShard(shardIndex, shardSize) {let _this = this;let file = _this.$refs.file.files[0];let start = (shardIndex - 1) * shardSize;	//当前分片起始位置let end = Math.min(file.size, start + shardSize); //当前分片结束位置let fileShard = file.slice(start, end); //从文件中截取当前的分片数据return fileShard;},selectFile() {let _this = this;$("#" + _this.inputId + "-input").trigger("click");}}
}
</script>
二、阿里云OSS

官网:https://www.aliyun.com

2.1. 注册阿里云

https://account.aliyun.com/register/register.htm
在这里插入图片描述

2.2. 开通OSS

在这里插入图片描述
在这里插入图片描述

2.3. 进入管控台

在这里插入图片描述
在这里插入图片描述

2.4. 创建 Bucket

在这里插入图片描述读写权限选择【公共读】,意思是都可以或者有权限看,没其他特殊请求,其他的保持默认,点击确定即可
在这里插入图片描述
在这里插入图片描述

2.5. 创建OSS用户

在这里插入图片描述
或者
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.6. OSS权限

在这里插入图片描述
在这里插入图片描述

三、OSS Client 开发文档

https://www.aliyun.com/product/oss

3.1. OSS Client SDK

开发语言java 追加上传(断点续传已实现)
在这里插入图片描述
在这里插入图片描述

3.2. 限制

在这里插入图片描述

3.3. SDK Client

这里就是官网提供的java语言的SDK Client
在这里插入图片描述

四、后端部分

https://help.aliyun.com/document_detail/32009.html

4.1.依赖引入

在这里插入图片描述

      <!-- OSS Java SDK --><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.10.2</version></dependency>
4.2. 配置
# 应用名称
spring.application.name=file
# 应用端口
server.port=9003
# 注册到eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka# 请求访问前缀
server.servlet.context-path=/file# 本地存储静态文件路径
file.path=D:/file/imooc/course/
# 访问静态文件路径(用于文件回显或者文件下载)
file.domain=http://127.0.0.1:9000/file/f/# 文件大小(如果搭建大小超过此配置的大小或抛出异常)
spring.servlet.multipart.max-file-size=50MB
# 请求大小
spring.servlet.multipart.max-request-size=50MB# OSS 配置
oss.accessKeyId=xxx
oss.accessKeySecret=xxx
oss.endpoint=http://oss-cn-beijing.aliyuncs.com
oss.ossDomain=http://bucket名称.oss-cn-beijing.aliyuncs.com/
oss.bucket=xxx
  • oss.endpoint 和oss.ossDomain获取方式
    在这里插入图片描述

  • bucket 获取方式
    在这里插入图片描述

  • oss.accessKeyId和oss.accessKeySecret获取方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3. api接口
package com.course.file.controller.admin;import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.AppendObjectRequest;
import com.aliyun.oss.model.AppendObjectResult;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.vod.model.v20170321.GetMezzanineInfoResponse;
import com.course.server.dto.FileDto;
import com.course.server.dto.ResponseDto;
import com.course.server.enums.FileUseEnum;
import com.course.server.service.FileService;
import com.course.server.util.Base64ToMultipartFile;
import com.course.server.util.UuidUtil;
import com.course.server.util.VodUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.ByteArrayInputStream;@RequestMapping("/admin")
@RestController
public class OssController {public static final Logger LOG = LoggerFactory.getLogger(OssController.class);public static final String BUSINESS_NAME = "文件上传";@Value("${oss.accessKeyId}")private String accessKeyId;@Value("${oss.accessKeySecret}")private String accessKeySecret;@Value("${oss.endpoint}")private String endpoint;@Value("${oss.bucket}")private String bucket;@Value("${oss.ossDomain}")private String ossDomain;@Resourceprivate FileService fileService;/*** oss追加上传** @param fileDto* @return* @throws Exception*/@PostMapping("/oss-append")public ResponseDto fileUpload(@RequestBody FileDto fileDto) throws Exception {LOG.info("上传文件开始");//接收前端的归属文件类型  COURSE("C", "课程"), TEACHER("T", "讲师");String use = fileDto.getUse();// 为了支持一个文件上传多次,展示历史的不同版本,因此上传文件前,统一添加文件前缀,下载时,统一截取文件没那个前8位处理String key = fileDto.getKey();//分片索引,1表示第1个分片Integer shardIndex = fileDto.getShardIndex();// 文件分片大小 shardSize = 10 * 1024 * 1024;// 以10MB为一个分片Integer shardSize = fileDto.getShardSize();// 具体的文件 由于为了统一使用FileDto对象接收,默认接收类型是MultipartFile,这里现在接收类型是String ,前端将文件提前转成了Base64String shardBase64 = fileDto.getShard();// 将具体的文件在由Base64转成MultipartFile类型MultipartFile shard = Base64ToMultipartFile.base64ToMultipart(shardBase64);//接收前端的归属文件类型  COURSE("C", "课程"), TEACHER("T", "讲师");FileUseEnum useEnum = FileUseEnum.getByCode(use);//文件全名String filename = shard.getOriginalFilename();//如果文件夹不存在,则创建String dir = useEnum.name().toLowerCase();String path = new StringBuffer(dir).append("/").append(key).append(".").append(filename).toString();// course\6sfSqfOwzmik4A4icMYuUe.mp4// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);ObjectMetadata meta = new ObjectMetadata();// 指定上传的内容类型。meta.setContentType("text/plain");// 通过AppendObjectRequest设置多个参数。AppendObjectRequest appendObjectRequest = new AppendObjectRequest(bucket, path,new ByteArrayInputStream(shard.getBytes()), meta);// 通过AppendObjectRequest设置单个参数。// 设置Bucket名称。//appendObjectRequest.setBucketName(bucketName);// 设置Object名称。即不包含Bucket名称在内的Object的完整路径,例如example/test.txt。//appendObjectRequest.setKey(objectName);// 设置待追加的内容。可选类型包括InputStream类型和File类型。此处为InputStream类型。//appendObjectRequest.setInputStream(new ByteArrayInputStream(content1.getBytes()));// 设置待追加的内容。可选类型包括InputStream类型和File类型。此处为File类型。//appendObjectRequest.setFile(new File("D:\\localpath\\examplefile.txt"));// 指定文件的元信息,第一次追加时有效。//appendObjectRequest.setMetadata(meta);// 第一次追加。// 设置文件的追加位置。// appendObjectRequest.setPosition(0L);appendObjectRequest.setPosition((long) (shardIndex - 1) * shardSize);AppendObjectResult appendObjectResult = ossClient.appendObject(appendObjectRequest);// 文件的64位CRC值。此值根据ECMA-182标准计算得出System.out.println(appendObjectResult.getObjectCRC());// 关闭OSSClient。ossClient.shutdown();LOG.info("保存文件记录开始");fileDto.setPath(path);fileService.save(fileDto);ResponseDto responseDto = new ResponseDto();// 文件OSS地址存储到fileDto,统一返回前端fileDto.setPath(ossDomain + path);responseDto.setContent(fileDto);return responseDto;}/*** 断点续传检查** @param key* @return* @throws Exception*/@GetMapping("/check/oss/{key}")public ResponseDto check(@PathVariable String key) throws Exception {LOG.info("检查上传分片开始:{}", key);ResponseDto responseDto = new ResponseDto();FileDto fileDto = fileService.findByKey(key);if (fileDto != null) {fileDto.setPath(ossDomain + fileDto.getPath());}responseDto.setContent(fileDto);return responseDto;}
}

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

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

相关文章

使用容器服务支持开发者快速搭建小程序后端服务

场景描述 小程序作为一种新的开放能力被广泛应用于各业务场景&#xff0c;开发者可以快速地开发一个小程序&#xff0c;在微信可便捷地获取和传播&#xff0c;同时依赖微信平台原生能力获取出色的使用体验。 阿里云容器服务结合阿里云云原生能力帮助客户快速构建小程序的后端服…

利用对象存储多种方式 保障OSS数据安全

本文介绍如何通过对象存储OSS提供的加密、访问控制、日志与监控及数据保护等多种方式来保障OSS的数据安全性。 加密 OSS提供服务器端加密、客户端加密以及数据传输加密三种数据加密方式。 服务器端加密 OSS通过服务器端加密机制&#xff0c;提供静态数据保护。适合于对于文件…

征战云时代,为什么安全是关键命题?

从左到右&#xff1a;李亮&#xff08;主持人&#xff09;、张美波、蒋涛、韦青扫描上方二维码直达精彩回顾整理 | 伍杏玲出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;全球数字化转型浪潮不断推进下&#xff0c;企业上云步伐加快&#xff0c;在这个过程中不少企…

Vue Bootstrap 静态服务器 实现文件追加上传、断点续传、极速秒传

公司实现文件上传技术选型采用后端SpringBoot/Cloud&#xff0c;前端vue Bootstrap &#xff0c;阿里云OSS作为文件存储&#xff0c;大文件上传功能单独抽取封装大文件上传组件&#xff0c;可供所有的大文件的操作。 后端框架版本SpringBoot2.5.6Spring-Cloud2020.0.4mysql8.0.…

如何调用API管理您的云上资源

欢迎您选择云服务器&#xff08;Elastic Compute Service&#xff0c;ECS&#xff09;&#xff0c;如果您熟悉网络服务协议和一种以上编程语言&#xff0c;推荐您调用API管理您的云上资源和开发自己的应用程序。 使用限制 根据您使用云服务器ECS的情况而变化&#xff0c;您能…

图片安全检测和数据跨境复制,规避违规风险

场景描述 本实践通过搭建WordPress博客系统&#xff0c;向用户展示如何将图片、附件等静态资源上传到阿里云OSS&#xff0c;并通过阿里云CDN进行加速&#xff0c;同时演示了基于函数计算托管函数完成OSS存储空间中数据的跨境复制&#xff0c;基于阿里云内容安全对OSS图片进行违…

Vue Bootstrap 静态服务器 实现文件上传

公司实现文件上传技术选型采用后端SpringBoot/Cloud&#xff0c;前端vue Bootstrap &#xff0c;静态服务器作为文件存储&#xff0c;文件上传功能单独抽取封装文件上传组件&#xff0c;可供所有的文件的操作。 后端框架版本SpringBoot2.5.6Spring-Cloud2020.0.4mysql8.0.26pag…

网络安全防护部署,升级支持IPv6

场景描述 关键信息基础设施支持IPv6已经上升到国家战略&#xff0c;公共云基础设施为云上客户系统支持IPv6提供了完整的解决方案及产品。本最佳实践是在现有WAF和高防IP作为网络安全架构基础上&#xff0c;如何升级支持IPv6&#xff0c;同时满足云上和线下IDC需求&#xff0c;…

500道Java 必备面试题答案(过后即删)

每个技术人都有个大厂梦&#xff0c;我觉得这很正常&#xff0c;并不是饭后的谈资而是每个技术人的追求。像阿里、腾讯、美团、字节跳动、京东等等的技术氛围与技术规范度还是要明显优于一些创业型公司/小公司&#xff0c;如果说能够在这样的公司锻炼几年&#xff0c;相信对自己…

简单七步,快速入门 JMeter原生压测

导入本地的 JMeter 原生脚本&#xff0c;即可快速发起一次压测。 前提条件 JMeter 脚本需要在本地调试成功。请同时上传依赖的 CSV 或者 JAR 文件。CSV 文件的 File Name 只需要文件名&#xff0c;不要带路径&#xff0c;因为上传之后和 JMX 脚本在同目录下。 限制条件 最大…

创建VPC前,网络规划的4个问题你弄清了吗?

在创建VPC和交换机前&#xff0c;您需要结合具体的业务规划VPC和交换机的数量及网段等。 应该使用几个VPC&#xff1f;应该使用几个交换机&#xff1f;应该选择什么网段&#xff1f;VPC与VPC互通或VPC与本地数据中心互通有什么要求&#xff1f; 应该使用几个VPC&#xff1f; …

移动云2020 H1营收44.57亿元,同比增长556.4%

近日&#xff0c;中国移动《2020年中期业绩报告》发布&#xff0c;中国移动盈利能力继续保持全球一流运营商的领先水平&#xff0c;中国移动H1营运收入3899亿元人民币&#xff0c;同比增长0.1%&#xff1b;净利润558亿元人民币&#xff0c;同比下降0.5%&#xff1b;其中&#x…

中小型互联网企业迁移上云,保证云端资源安全迁移

场景描述 本方案主要适用于中小型互联网企业&#xff0c;从他云迁移到阿里云的最佳实践。方案中会概述网络规划、服务器、数据库、存储数据、 Kafka和镜像数据的迁移方案描述。同时在实践环节&#xff0c;增加服务器和RDS的迁移操作演练和验证。 解决问题 提供服务器、数据库…

Thymeleaf Error resolving template [index],template might not exist or might not be accessible

文章目录1. 导入依赖2. 配置2. 视图层3. 创建页面4. 检查target5. 表达式使用Thymeleaf 一共5步&#xff0c;检查一下看看是否符合规范 1. 导入依赖 <!--模板引擎--><dependency><groupId>org.springframework.boot</groupId><artifactId>sprin…

什么?一个核同时执行两个线程?

作者 | 轩辕之风来源 | 编程技术宇宙头图 | CSDN付费下载自视觉中国CPU里的时间Hi&#xff0c;好久不见&#xff0c;我是CPU一号车间的阿Q&#xff0c;不认识我的话&#xff0c;可以看看&#xff1a;完了&#xff01;CPU一味求快出事儿了&#xff01;真的是好久不见了&#xff…

海纳威上云 构建数字化透明工厂

“海纳威通过阿里巴巴的宜搭平台&#xff0c;开发了生产计划分配、计时计件、考勤与工资计算系统。宜搭的这种模式&#xff0c;可以按照我们自己的想法来设计软件&#xff0c;贴合现场业务来开发和调整&#xff0c;满足了管理诉求。效率很高&#xff0c;成本可控&#xff0c;和…

通用网站备案常见的备案场景及要求

当网站托管于中国大陆境内的服务器上时&#xff0c;网站需申请ICP备案。本文为您介绍常见网站的备案场景及备案要求。 通用网站备案 此种场景下&#xff1a; 托管于大陆境内服务器的网站需备案&#xff0c;托管于中国大陆境外服务器的网站无需备案。网站备案将会审核网站的三…

如何利用DTS数据同步功能,快速创建数据同步作业

数据传输服务DTS&#xff08;Data Transmission Service&#xff09;提供的数据同步功能简单易用&#xff0c;您只需在控制台上进行简单操作&#xff0c;即可完成整个数据同步作业的配置。 注意事项 本文仅简单介绍数据同步作业的通用配置流程&#xff0c;不同的数据源在配置…

震惊!程序员要放弃 Python 了!?发生了啥?

听说 Python 很难学&#xff1f;难在哪里&#xff1f;听说学完不知道做什么&#xff1f;为什么&#xff1f;Python 该怎么学&#xff1f;好不好学&#xff1f;学完做什么&#xff1f;许多人都会遇到了下面这些问题——1. 没经验根本不知道从何学起&#xff0c;而且应用方向太多…

如何快速完成企业应用IPv6改造

场景描述 对于IPv4-only的云服务器&#xff0c;通过DNS域名解析IPv6转换服务&#xff0c;快速支持对外提供IPv6访问能力。对于双栈云服务器&#xff0c;构建应用环境。 解决问题 改造技术复杂度高改造任务时间紧改造所需人力和设备成本较高 产品列表 虚拟专用网VPC弹性计算E…