<tbody id="j99e4"></tbody>

<dd id="j99e4"></dd>

  • <button id="j99e4"><object id="j99e4"></object></button>
      1. <th id="j99e4"></th>
        <button id="j99e4"><acronym id="j99e4"></acronym></button><rp id="j99e4"><object id="j99e4"><input id="j99e4"></input></object></rp>
        當前位置:首頁 > IT技術 > Web編程 > 正文

        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

        亚洲人成图偷偷小说_亚洲图片小说激情综合_国产精品亚洲自在线播放页码_久久综合亚洲色hezyo国产
        <tbody id="j99e4"></tbody>

        <dd id="j99e4"></dd>

      2. <button id="j99e4"><object id="j99e4"></object></button>
          1. <th id="j99e4"></th>
            <button id="j99e4"><acronym id="j99e4"></acronym></button><rp id="j99e4"><object id="j99e4"><input id="j99e4"></input></object></rp>