<p id="g32nn"></p>
    1. <acronym id="g32nn"><strong id="g32nn"></strong></acronym>
      <pre id="g32nn"></pre>

      <table id="g32nn"><option id="g32nn"></option></table>

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳
          2022-09-06 22:35:06


          公司實現文件上傳技術選型采用后端SpringBoot/Cloud,前端vue Bootstrap ,阿里云OSS作為文件存儲,大文件上傳功能單獨抽取封裝大文件上傳組件,可供所有的大文件的操作。

          后端框架

          版本

          SpringBoot

          2.5.6

          Spring-Cloud

          2020.0.4

          mysql

          8.0.26

          pagehelper

          1.3.1

          Mybatis

          2.2.0

          Redis

          5.0

          Fastjson

          1.2.78

          前端框架

          版本

          Vue

          2.6.11

          axios

          0.24.0

          vue-router

          3.5.3

          Bootstrap

          4.6.2

          文章目錄

          一、前端部分
          1. 小節頁面

          小節頁面作為文件上傳父頁面

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java

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

          Loading.show()
          _this.$api.post(process.env.VUE_APP_SERVER + '/business/admin/section/save', _this.section).then((res) => {
          Loading.hide()
          let resp = res.data
          if (resp.success) {
          $("#form-modal").modal("hide")
          _this.list(1)
          Toast.success("保存成功!")
          } else {
          Toast.warning(resp.message)
          }
          })
          },

          afterUpload(resp) {
          let _this = this
          let 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: 1901173357457
          lastModifiedDate: Tue May 27 2099 14:49:17 GMT+0800 (中國標準時間) {}
          webkitRelativePath: ""
          size: 37415970
          type: "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()));
          /*
          d41d8cd98f00b204e9800998ecf8427e
          2.8194976848941264e+38
          6sfSqfOwzmik4A4icMYuUe
          */

          // 判斷文件格式
          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??

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_vue.js_02

          2.2. 開通OSS

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_vue.js_03


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java_04

          2.3. 進入管控臺

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java_05


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_ide_06

          2.4. 創建 Bucket

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_上傳_07

          讀寫權限選擇【公共讀】,意思是都可以或者有權限看,沒其他特殊請求,其他的保持默認,點擊確定即可

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_上傳_08


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_vue.js_09

          2.5. 創建OSS用戶

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_ide_10


          或者

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_上傳_11


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_ide_12


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java_13

          2.6. OSS權限

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_vue.js_14


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java_15

          三、OSS Client 開發文檔

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

          3.1. OSS Client SDK

          開發語言java 追加上傳(斷點續傳已實現)

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_ide_16


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_上傳_17

          3.2. 限制

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_bootstrap_18

          3.3. SDK Client

          這里就是官網提供的java語言的SDK Client

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java_19

          四、后端部分

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

          4.1.依賴引入

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java_20

          <!-- 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獲取方式
          • Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_ide_21

          • bucket 獲取方式
          • Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_上傳_22

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

          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_vue.js_23


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_java_24


          Vue Bootstrap OSS 實現文件追加上傳、斷點續傳、極速秒傳_上傳_25

          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;

          @Resource
          private 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 ,前端將文件提前轉成了Base64
          String 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();// course6sfSqfOwzmik4A4icMYuUe.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);
          // 設置待追加的內容??蛇x類型包括InputStream類型和File類型。此處為InputStream類型。
          //appendObjectRequest.setInputStream(new ByteArrayInputStream(content1.getBytes()));
          // 設置待追加的內容??蛇x類型包括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;
          }
          }


          本文摘自 :https://blog.51cto.com/g


          更多科技新聞 ......

          97久久久久人妻精品专区_国产成人精品视频导航_国产色诱视频在线播放网站_97午夜理论电影影院
          <p id="g32nn"></p>
          1. <acronym id="g32nn"><strong id="g32nn"></strong></acronym>
            <pre id="g32nn"></pre>

            <table id="g32nn"><option id="g32nn"></option></table>