当前位置:   article > 正文

vue自定义封装大文件分片上传组件,带上传进度条显示,断点续传,视频播放器组件,原生JS的AJAX封装,Promise异步变同步_vue 实现上传视频 进度带暂停

vue 实现上传视频 进度带暂停

说明:

请求用了自定义封装的原生js的ajax请求和axios
视频播放用了vue-video-player插件
文件的md5编码用了sparkmd5.js
读取二进制文件用的客户端自带的FileReader接口

封装的分片上传和断点续传组件:

<template>
    <div class="biguploadfile">
        <div class="videoBox">
            <div class="progressBox" v-if="showProgress">
                <span class="progressItem" ref="progressItem"></span>
            </div>
            <videoPlay :src="fileBaseUrl+src" poster='' v-if="showVideo && !pauseStatus && !showProgress"></videoPlay>
        </div>
        <input type="file" class="bigFile" @change="computedSliceMd5" ref="file" name="file">
        <div class="btns">
            <Button type="primary" v-if="!pauseStatus" @click="toUpload">上传</Button>
            <Button type="default" v-if="pauseStatus" @click="pauseUpload">取消</Button>
        </div>
    </div>
</template>
<script>
import SparkMD5 from "spark-md5"; //获取二进制文件md5编码
import videoPlay from "@/components/videoPlayer"; //利用vue-video-player插件封装的播放器
import Ajax from "@/plugins/ajax.js"; //封装的原生JS ajax请求
import api from "@/api/commonApi.js"; //本地用的接口
import code from "@/config/base.js"; //这是项目中自定义的参数
export default {
    data() {
        return {
            showVideo: false,
            blobSlice: null,
            file: null,
            identifier: null,
            chunkSize: 1024000,
            chunks: 0,
            currentChunk: 0,
            spark: null,
            fileReader: null,
            tmpDataList: [],
            formDataList: [],
            uploadedList: [],
            start: 0,
            end: 0,
            headers: {},
            urlCode: code.urlCode.lectureDemand,
            xhr: null,
            pauseStatus: false,
            showProgress: false,
            percent: 0
        };
    },
    props: {
        src: { //利用props将上传的视频地址传回来,用于预览
            type: String,
            default: ""
        }
    },
    components: { videoPlay }, //播放器组件
    methods: {
        pauseUpload() { //取消上传
            this.xhr.abort();
            this.xhr = null;
        },
        toUpload() { //调用上传接口
            this.$refs.file.click();
        },
        checkMd5() { //断点续传需要用的方法,断点续传获取到的是已经上传过去的分片编号,已经上传过的分片直接跳过去
            this.showProgress = true;
            this.$axios
                .post(api.lecturedemanduploadfilemd5, { md5: this.identifier })
                .then(resp => {
                    if (resp.data.object.code == 2) {
                        let tmpIdsList = resp.data.object.ids;
                        tmpIdsList.map((el,index) => {
                            tmpIdsList[index] = parseInt(el);
                        });
                        this.uploadedList = tmpIdsList;
                    }
                    this.showVideo = false;
                    this.pauseStatus = true;
                    this.uploadFile(
                        1,
                        this.formDataList,
                        this.uploadedList,
                        this.chunks
                    );
                })
                .catch(err => {
                    this.initParam(0, "断点续传检查失败,请重试");
                });
        },
        computedSliceMd5() {  //获取文件的md5编码
            this.file = this.$refs.file.files[0];
            this.chunks = Math.ceil(this.file.size / this.chunkSize);
            this.fileReader.onload = e => {
                this.spark.append(e.target.result); // Append array buffer
                this.currentChunk++;
                if (this.currentChunk < this.chunks) {
                    this.loadNext();
                } else {
                    this.identifier = this.spark.end(); //文件的MD5身份标识
                    this.tmpDataList.map((el, i) => {
                        let formData = new FormData();
                        formData.append("filename", this.file.name);
                        formData.append("relativePath", this.file.name);
                        formData.append("totalSize", this.file.size);
                        formData.append("chunkSize", this.chunkSize);
                        formData.append("identifier", this.identifier);
                        formData.append("totalChunks", this.chunks);
                        formData.append("file", el.file);
                        formData.append("currentChunkSize", el.currentSize);
                        formData.append("chunkNumber", el.currentNum + 1);
                        formData.append("urlCode", this.urlCode);
                        this.formDataList.push(formData);
                    });
                    this.checkMd5();
                }
            };
            this.fileReader.onerror = function() {
                this.$Message.error("读取文件出错,请重试");
            };
            this.loadNext();
        },
        loadNext() { //文件切片
            (this.start = this.currentChunk * this.chunkSize),
                (this.end =
                    this.start + this.chunkSize >= this.file.size
                        ? this.file.size
                        : this.start + this.chunkSize);
            let pieceFile = this.blobSlice.call(
                this.file,
                this.start,
                this.end
            );
            pieceFile.name = this.file.name;
            let tmpObj = {
                file: pieceFile,
                currentSize: this.end - this.start,
                currentNum: this.currentChunk
            };
            this.tmpDataList.push(tmpObj);
            this.fileReader.readAsArrayBuffer(pieceFile);
        },
        uploadFile(n, dataList, uploadedList, chunks) { //利用promise异步变同步上传
            console.log("uploadedList",uploadedList);
            let status = uploadedList.indexOf(n) == -1 ? true : false;
            console.log("status",status);
            let key = n - 1;
            if (status) {
                new Promise((resolve, reject) => {
                    Ajax(
                        resolve,
                        reject,
                        "post",
                        api.lecturedemanduploadfile,
                        this.headers,
                        dataList[key],
                        this.xhrReturn,
                        this.getProgress
                    );
                })
                .then(resp => {
                    if(resp.code == "401"){
                        this.$router.push({path:"/login"});
                        return ;
                    }
                    if (resp.object.uploadStatus == 1) {
                        let tmpUrl = JSON.parse(resp.object.fileUrl)[0]
                            .fileName;
                        this.$emit("getUrl", tmpUrl);
                        this.success();
                    } else {
                        this.uploadFile(
                            n + 1,
                            this.formDataList,
                            this.uploadedList,
                            this.chunks
                        );
                    }
                })
                .catch(err => {
                    this.initParam(0, "上传失败");
                });
            }else{
                this.percent += Math.floor(100/this.chunks);
                this.changeProgress(1);
                this.uploadFile(
                    n + 1,
                    this.formDataList,
                    this.uploadedList,
                    this.chunks
                );
            }
        },
        getProgress(evt) { //ajax进度条回调方法
            if (evt.lengthComputable) {
                this.percent += Math.floor(
                    (evt.loaded * 100) / this.chunks / evt.total
                );
                if(this.percent>=99){
                    this.percent = 99;
                }
                this.changeProgress(1);
            } else {
                this.changeProgress(0);
            }
        },
        changeProgress(type) { //进度条展示
            if (type) {
                this.$refs.progressItem.innerHTML =
                    this.percent.toFixed(2) + "%";
                this.$refs.progressItem.style.width =
                    this.percent.toFixed(2) + "%";
            } else {
                this.$refs.progressItem.innerHTML = "上传失败";
            }
        },
        success() { //成功回调
            this.percent = 100;
            this.changeProgress(1);
            setTimeout(() => {
                this.initParam(1, "上传成功");
            }, 500);
        },
        failed(err) { //失败回调
            this.initParam(0, err);
        },
        initParam(type, msg) { //成功或失败之后的初始化
            this.percent = 0;
            this.pauseStatus = false;
            this.showProgress = false;
            this.file = null;
            this.identifier = null;
            this.chunks = 0;
            this.currentChunk = 0;
            this.tmpDataList = [];
            this.formDataList = [];
            this.uploadedList = [];
            this.start = 0;
            this.end = 0;
            this.$refs.file.value = "";
            if (type == 1) {
                this.showVideo = true;
                this.$Message.success(msg);
            } else {
                this.showVideo = false;
                this.$Message.error(msg);
            }
        },
        xhrReturn(xhr) {
            this.xhr = xhr;
        }
    },
    created() { //一些变量的初始化
        if (Boolean(this.src)) {
            this.showVideo = true;
        }
        this.blobSlice =
            window.File.prototype.slice ||
            window.File.prototype.mozSlice ||
            window.File.prototype.webkitSlice;
        this.spark = new SparkMD5.ArrayBuffer();
        this.fileReader = new FileReader();
        this.percent = 0;
    }
};
</script>
<style lang="less" scoped>
.biguploadfile {
    display: inline-block;
    .bigFile {
        display: none;
    }
    .progressBox {
        display: inline-block;
        width: 200px;
        height: 20px;
        background-color: gray;
        vertical-align: middle;
    }
    .progressItem {
        display: inline-block;
        height: 20px;
        background-color: #2d8cf0;
        font-weight: bold;
        color:#ffffff;
    }
    .btns {
        display: inline-block;
        vertical-align: middle;
    }
    .videoBox {
        display: inline-block;
        vertical-align: middle;
    }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292

可参考的自定义封装的Ajax

function getXhr(){
    let xhr=null;
    if(window.XMLHttpRequest){
        xhr = new XMLHttpRequest();
    } else {
        //为了兼容IE6
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    return xhr;
}
export default function Ajax(resolve,reject,type, url, headers, data, xhrReturn=()=>{}, progress=()=>{}){ //参数可封装成对象形式的,用着方便,这里就不改了
    /* 
    ajax暂停之后不能重新发送请求,需要创建新的xhr对象重新发送请求
    */
    let xhr = getXhr();

    xhrReturn(xhr);//将xhr对象返回,用于取消上传

    type = type.toUpperCase();
    
    let reg = new RegExp('\\?',"g");
    if(reg.test(url)){
        reject("url地址传递错误");
        return ;
    }
    let random = Math.random();
    url += "?"+random;
    if(type == 'GET'){
        reject("不支持GET提交方式");
    } else if(type == 'POST'){
        xhr.open('POST', url, true);
        
        if(typeof headers == 'object'){
            for(let key in headers){
                // 如果需要像 html 表单那样 POST 数据,请使用 setRequestHeader() 来添加 http 头。
                xhr.setRequestHeader(key, headers[key]);
            }
        }else{
            reject("请求头设置有误");
            return ;
        }
        xhr.withCredentials = false;
        
        xhr.upload.onprogress = progress;
        xhr.onabort = ()=>{
            xhr = null;
        };
        xhr.send(data);
    }
 
    // 处理返回数据
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
            if(xhr.status == 200){
                let resp = JSON.parse(xhr.responseText);
                resolve(resp);
            } else {
                reject("提交失败,请重试");
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

可参考的videoPlayer组件

<template>
    <div class="container">
        <div class="video-box" :style="videoStyle">
            <video-player
                class="video-player vjs-custom-skin video-viewport"
                ref="videoPlayer"
                :playsinline="true"
                :options="playerOptions"
                @play="onPlayerPlay($event)"
                @pause="onPlayerPause($event)"
            ></video-player>
        </div>
    </div>
</template>
<script>
import cookie from "./../utils/cookie.js";
import api from "@/api/commonApi";
export default {
    data() {
        return {
            playerOptions: {
                playbackRates: [0.5, 1.0, 1.5, 2.0, 3.0, 6.0, 12.0], //播放速度
                autoplay: false, //如果true,浏览器准备好时开始回放。
                muted: false, // 默认情况下将会消除任何音频。
                loop: false, // 导致视频一结束就重新开始。
                preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
                language: "zh-CN",
                aspectRatio: "4:3", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
                fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
                sources: [
                    {
                        type: "video/mp4",
                        src: "" //你的视频地址(必填)
                    }
                ],
                poster: "", //你的封面地址
                width: document.documentElement.clientWidth,
                notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
                controlBar: {
                    timeDivider: true,
                    durationDisplay: true,
                    remainingTimeDisplay: false,
                    fullscreenToggle: true //全屏按钮
                }
            }
        };
    },
    props: {
        src: {
            type:String,
            default: ""
        },
        poster: {
            type:String,
            default: "./static/img/heicon.jpg"
        },
        videoStyle:{
            type:Object,
            default:()=>{
                return { width: "400px", height: "300px" };
            }
        }
    },
    methods: {
        onPlayerPlay(player) {
            console.log("触发播放");
        },
        onPlayerPause(player) {
            console.log("触发暂停");
        }
    },
    computed: {
        player() {//暂时没用到
            return this.$refs.videoPlayer.player;
        }
    },
    mounted() {
        this.playerOptions.sources[0].src = this.src;
        this.playerOptions.poster = this.poster;
    }
};
</script>
<style lang="less" scoped>
.container {
    display:inline-block;
    .video-box {
        .video-viewport {
            width: 100%;
            height: 100%;
        }
    }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

调用:

<template>
    <div>
    	<!-- 用props将上传的视频地址传递过去,还有一个方法 -->
        <bigUploadFile :src="src" @getUrl="getUploadUrl"></bigUploadFile>
    </div>
</template>
<script>
import bigUploadFile from "@/components/bigUploadFile";
export default {
    data() {
        return {
            src: ""
        };
    },
    created() {},
    components: {
        bigUploadFile
    },
    methods: {
        //获取上传的url
        getUploadUrl(url) {
            console.log("传递过来的url", url);
            this.src = url;
        }
    }
};
</script>
<style lang="less">
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

作者:喜大普奔⁶⁶⁶
来源:CSDN
原文:https://blog.csdn.net/qq_38652871/article/details/88229749
版权声明:本文为博主原创文章,转载请附上博文链接!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/130235
推荐阅读
相关标签