_花海 2024-10-14 19:50 采纳率: 0%
浏览 3
问题最晚将于10月22日00:00点结题

elementui上传结合oss接口断点续传,现在只差停止上传和继续上传,各大精英看下

elementui上传结合oss接口断点续传,现在只差停止上传和继续上传,各大精英帮忙看下

<template>
    <div id="templateUpload" class="posi-r" style="padding-bottom: 110px;">
        <!-- <div class="posi-a left0 top0 right0 bottom0" style="z-index: 100;" @click="updateIngEvent"
            v-show="pageData.updateIng"></div> -->
        <div class="title">2-资源上传</div>
        <div class="cx-cc pb20 mb20 posi-r" id="contractUploadBox"
            style="background-color:rgba(0, 163, 255, 0.03);border: 1px dashed #D9D9DA;">
            <img class="w120 mb10" src="@/assets/img/upload_yun.png" alt="">
            <p class="color-333 ft16 mb20">点击或文件拖拽到这里上传</p>
            <p class="ft16 color-fff ft12 bg-main pt10 pb10 pl40 pr40 c_p mb12 bdr4">点击上传</p>
            <p class="ft12 color-999">仅支持MP4视频文件,上传添加不超过500个,单次累计上传文件不超过30M</p>
            <input class="posi-a left0 bottom0 top0 right0 opacity0" accept="video/mp4" multiple
                type="file" name="" @change="inputUploadChange">
        </div>
        <div class="rx-bc mb12">
            <div class="rx-sc">
                <el-dropdown class="mr12">
                    <span class="el-dropdown-link">
                        <el-button type="primary">极速上传</el-button>
                    </span>
                    <template #dropdown>
                        <el-dropdown-menu>
                            <el-dropdown-item command="1">
                                <div class="posi-r">
                                    上传文件夹
                                    <input class="posi-a left0 bottom0 top0 right0 opacity0" webkitdirectory multiple
                                        type="file" name="" @change="inputUploadChange">
                                </div>
                            </el-dropdown-item>
                            <el-dropdown-item command="2">
                                <div class="posi-r">
                                    上传文件
                                    <input class="posi-a left0 bottom0 top0 right0 opacity0"
                                        accept="video/mp4" multiple type="file" name=""
                                        @change="inputUploadChange">
                                </div>
                            </el-dropdown-item>
                        </el-dropdown-menu>
                    </template>
                </el-dropdown>
                <el-dropdown @command="batchBtnChange">
                    <span class="el-dropdown-link">
                        <el-button>批量操作</el-button>
                    </span>
                    <template #dropdown>
                        <el-dropdown-menu>
                            <el-dropdown-item command="1">批量删除</el-dropdown-item>
                        </el-dropdown-menu>
                    </template>
                </el-dropdown>
            </div>
            <div>
                <span class="ft14">已上传/总上传:</span>
                <span class="color-03A3FF">{{ fileDatas.list.length }}</span>
                <span class="color-999">/500</span>
            </div>
        </div>
        <el-table class="ww100" :data="fileDatas.list">
            <el-table-column label="" width="55">
                <template #default="scope">
                    <el-checkbox v-model="scope.row.check"></el-checkbox>
                </template>
            </el-table-column>
            <el-table-column prop="typeName" label="视频文件" width="100">
            </el-table-column>
            <el-table-column prop="name" label="资源名称">
                <template #default="scope">
                    <div class="color-333 ft14 mb10">{{ scope.row.name }}</div>
                </template>
            </el-table-column>
            <el-table-column label="单集时长">
                <template #default="scope">
                    <div>{{ formatVideoDuration(scope.row.videoDuration) }}</div>
                </template>
            </el-table-column>
            <el-table-column label="上传状态">
                <template #default="scope">
                    <div class="rx-sc" v-if="scope.row.state == 1">
                        <el-icon color="#25D384">
                            <CircleCheck />
                        </el-icon>
                        <span class="color-25D384 ft14 ml5">已完成</span>
                    </div>
                    <div class="rx-sc" v-else>
                        <div class="flex1 mr12">
                            <div class="color-666 mb10">{{ scope.row.state == 0 ? '等待中' : scope.row.state == 2 ? '上传失败' :
                                '上传中' }}
                            </div>
                            <div class="posi-r bdr100 bg-C6DFF5" style="height:5px">
                                <div class="posi-a bdr100 left0 top0 bottom0 bg-0195ED" :style="{ width: scope.row.width }">
                                </div>
                            </div>
                        </div>
                        <div class="color-03A3FF">{{ scope.row.estimated }}</div>
                    </div>
                </template>
            </el-table-column>
            <el-table-column label="操作">
                <template #default="scope">
                    <a href="javascript:;" v-if="scope.row.state == 1" class="t_d_u color-409EFF tool_btn color-red"
                        @click="remove(scope.$index)">删除</a>
                    <a href="javascript:;"  class="t_d_u color-409EFF tool_btn color-red"
                        @click="stop(scope.row)">停止上传</a>
                    <a href="javascript:;"  class="t_d_u color-main tool_btn color-red"
                        @click="continueUpld(scope.row)">继续上传</a>
                    <a href="javascript:;" v-if="scope.row.state == 2" class="t_d_u color-main tool_btn color-red"
                        @click="anew(scope.row)">重新上传</a>
                    <span v-if="scope.row.state == 0">-</span>
                </template>
            </el-table-column>
        </el-table>
        <div v-show="pageData.width" class="rx-ec bg-FAFAFA posi-f bottom0" :style="{ width: pageData.width + 'px' }"
            style="z-index: 400;">
            <div class="color-000 ft16 pt20 pb20 pl40 pr40 c_p" style="background-color: #EAEAEA;" @click="delBack">取消</div>
            <div class="color-fff ft16 bg-main pt20 pb20 pl40 pr40 c_p" @click="saveEvent">确定</div>
        </div>
        <!-- 上传失败 -->
        <el-dialog v-model="errorFileData.show" title="文件上传失败提示" width="800px"
            :before-close="() => { errorFileData.show = false }">
            <div class="p20">
                <el-table class="ww100" :data="errorFileData.list">
                    <el-table-column prop="name" label="文件名称" />
                    <el-table-column prop="type" label="文件类型" />
                    <el-table-column label="失败原因">
                        <template #default="scope">
                            文件格式有误,请检查后重新上传,支持视频格式:mp4,单集限制30M以内
                        </template>
                    </el-table-column>
                </el-table>
            </div>
        </el-dialog>
    </div>
</template>
<script setup>

import { ref, reactive, onMounted } from 'vue';
import axios from 'axios'
import {
    ElMessage
} from 'element-plus'

import { useMainPage } from '@/store/modules/app.js';
const mainPage = useMainPage();

const pageData = reactive({
    updateIng: false,
    width: 0,
    options: [],
    teacherList: [],
    unitId: id || '',
    teacherId: '',
    teacherName: '',
    unitArr: [],
    newData:[]
})

// 时分秒
function formatVideoDuration(cellValue) { 
    if(!cellValue) return '' 
    let minutes = Math.floor(cellValue / 60);  
    let seconds = cellValue % 60;  
    return `${minutes}${seconds.toFixed(0)}秒`;  
}

const fileDatas = reactive({
    list: []
})
function inputUploadChange(e) {
    var arr = Array.from(e.target.files);
    var videos = [], orders = [];
    arr.forEach(n => {
        if (n.type.indexOf('mp4') > -1 && n.size / 1024 / 1024 < 30) {
            let isItem = fileDatas.list.find(item=>item.nameStatus==n.name) // 如果上传的剧集中已经存在提示已经存在
            if(isItem){
                return ElMessage.warning(`${n.name}该视频已经上传!`);
            }
            videos.push(n);
        } else {
            orders.push(n);
        }
    })
    if (orders.length > 0) {
        errorFile(orders);
    }
    
    if(videos.length > 0) {
        uploadStart(videos);
    }
}
    
function remove(index) {
    fileDatas.list.splice(index, 1);
}
var uploadArr = [], oldArr = [];
function uploadStart(files) {
    if (files.length == 0) {
        ElMessage.warning("未检测到文件");
        return false;
    }
    pageData.updateIng = true;
    var beforeNum = fileDatas.list.length;//记录这次上传之前 已经上传了多少个
    files.forEach(n => {
        n.numIndex = beforeNum;
        fileDatas.list.push({
            numIndex: beforeNum,//记录索引
            url: '',
            nameStatus:n.name,
            name: n.name.split('.')[0],
            typeName:n.name.split('.')[1],
            newSize: (n.size / 1024 / 1024).toFixed(2) + 'MB',
            size: n.size,
            check: false,
            teacherId: pageData.teacherId,
            width: '0%',
            estimated: '0MB/s',
            state: 0,
            videoDuration: 0
        })
        
        beforeNum++;
    })
    uploadArr = files.splice(0, 1);
    oldArr = files;
    startUpload(uploadArr);
}
var timer = '', overNum = 0;//上传完几个
function overUpload() {
    overNum++;
    clearTimeout(timer);
    timer = setTimeout(() => {
        uploadArr.splice(0, overNum); //去掉上传队列中上传完的
        if (oldArr.length) {
            var arr = oldArr.splice(0, overNum); //拿几个补到到上传队列
            overNum = 0;//初始上传完成数
            uploadArr = [...arr, ...uploadArr];
            startUpload(arr);//开始上传
        }
    }, 500)
}
function startUpload(arr) {
    for (var i = 0; i < arr.length; i++) {
        uploadFun(arr[i]);
    }
}

let uploadProgress = {}; // 存储每个文件的上传进度  
let cancelTokens = {}; // 存储每个文件的取消令牌  

// 停止上传
function stop(val){
    
}
// 继续上传
function continueUpld(val){
    
}
function uploadFun(item) {
    let data = pageData.newData.find(val=>val.name==item.name) //记录上传视频item
    if(!data){
        pageData.newData.push(item)
    }
    let nowIndex = item.numIndex;//i+beforeNum; //取到当前正在上传的索引
    getVideoTime(item, (videoDuration) => {
        fileDatas.list[nowIndex].videoDuration = videoDuration;
    })
    fileDatas.list[nowIndex].state = 3;
    commonUpload({ fileData: item }, (formData, ossUrl, fileData) => {
        let param = new FormData();
        for (var k in formData) {
            param.append(k, formData[k]);
        }
        param.append("file", fileData.fileData);
        let uploadTime = (new Date()).getTime();
        let oldByte = 0;
        axios.request({
            url: ossUrl,
            method: 'post',
            headers: { "Content-Type": "multipart/form-data" },
            data: param,
            onUploadProgress: function (progressEvent) {
                var { progress, estimated, loaded } = progressEvent;
                fileDatas.list[nowIndex].width = progress * 100 + '%';
                let nowTime = (new Date()).getTime();
                if (nowTime - uploadTime > 1000) {
                    let nowByte = (loaded - oldByte) / 1024 / 1024;
                    fileDatas.list[nowIndex].estimated = nowByte.toFixed(2) + 'MB/s';
                    uploadTime = nowTime;
                    oldByte = loaded;
                }
            },
        }).then((res) => {
            if (res.code == 0) {
                fileDatas.list[nowIndex].state = 1;
                fileDatas.list[nowIndex].url = res.results.wholeUrl;
            } else {
                fileDatas.list[nowIndex].state = 2;
            }
            var success = fileDatas.list.filter(n => n.state > 0);
            if (success.length == fileDatas.list.length) { //上传完了
                pageData.updateIng = false;
            } else {
                overUpload();
            }

        })
    })
}
function getVideoTime(file, callback) {
    var reader = new FileReader();
    reader.onload = function (e) {
        var video = document.createElement('video');
        video.preload = 'metadata';

        // 当视频的元数据已加载后,获取视频时长
        video.onloadedmetadata = function () {
            window.URL.revokeObjectURL(video.src);
            callback && callback(video.duration);
        };

        video.src = URL.createObjectURL(file);
    };
    reader.readAsArrayBuffer(file);
}
//上传失败提示
const errorFileData = reactive({
    show: false,
    list: []
})
function errorFile(list) {
    if (list.length > 0) {
        errorFileData.list = list;
        errorFileData.show = true;
    }
}
</script>


commonUpload是调用的是oss上传接口

  • 写回答

9条回答 默认 最新

  • 专家-皆知 2024-10-14 19:51
    关注

    参考免费微信小程序:皆我百晓生 作答,经由专家-皆知整理回复,如问题解决,望采纳;如仍有疑问,请评论回复。

    要实现断点续传、停止上传和继续上传的功能,关键在于管理上传任务的状态和使用可中断的上传请求。在您提供的代码基础上,我们可以添加一些逻辑来实现这些功能。

    断点续传核心思路

    1. 存储上传状态:对于每个上传任务,需要记录其上传的进度(比如上传的字节范围)。
    2. 停止上传:实际上是标记任务为暂停状态,并保存当前进度。
    3. 继续上传:根据之前保存的进度信息,从特定字节位置开始上传。

    修改建议

    1. 添加或修改数据结构以保存状态

    首先,我们需要在fileDatas.list中的每个对象里增加字段来记录上传状态,比如uploadId用于唯一标识一个上传任务,以及byteOffset记录已上传的字节数。

    // 在fileDatas.list每个对象中添加或确保有这些字段
    {
        ...
        uploadId: null, // 上传任务的唯一标识
        byteOffset: 0,  // 已上传的字节数,用于断点续传
    }
    

    2. commonUpload改造

    需要修改commonUpload以支持暂停和恢复上传,这通常涉及到OSS的分块上传策略和使用特定API来检查或继续上传特定块。

    由于实际的commonUpload函数和OSS接口细节没有提供,我将给出一个概念性的修改思路,不包括具体实现细节:

    // 假设commonUpload接受额外参数:byteOffset, uploadId
    function commonUpload({ fileData, byteOffset, uploadId }, callback) {
        // 根据byteOffset和uploadId调整上传逻辑,如果是新上传则初始化
        // 如果是继续上传,则使用OSS的续传接口
        // 实际实现会依赖于OSS SDK的具体方法,例如阿里云OSS的uploadPart等
    }
    

    3. 停止上传实现

    停止上传时,主要是标记任务状态,并保留当前进度信息,无需真正中断HTTP请求(因为实际中断可能需要OSS SDK的支持)。这里简化处理,仅做标记。

    function stop(item) {
        if (!item.uploadId) {
            console.error("该文件未开始上传或无上传任务ID");
            return;
        }
        // 标记为暂停状态
        item.state = 4; // 假设4表示暂停
        // 如果需要,可以在这里保存到服务器或本地存储的进度信息
    }
    

    4. 继续上传实现

    继续上传基于之前保存的byteOffsetuploadId,调用commonUpload时传递这些信息。

    function continueUpld(item) {
        if (!item.uploadId || item.byteOffset === 0) {
            console.error("无法继续上传,未保存有效的上传状态");
            return;
        }
        // 调用commonUpload,尝试从断点处继续上传
        commonUpload({ fileData: item, byteOffset: item.byteOffset, uploadId: item.uploadId }, (formData, ossUrl, fileData) => {
            // 后面的逻辑保持不变
        });
    }
    

    注意

    • 实现断点续传需要OSS服务的支持,确保OSS配置允许分块上传和查询上传状态。
    • 上述代码仅为指导思路,实际应用中需结合具体的OSS SDK接口文档进行调整。
    • 对于如何具体实现commonUpload中的分块上传、续传逻辑,需要查阅阿里云OSS或其他云服务商的官方文档,了解如何使用他们的SDK来实现这些高级功能。

    由于没有具体的OSS SDK代码示例,以上代码和思路是基于通用概念给出的,实际编码时请参考相应的SDK文档进行详细实现。

    评论

报告相同问题?

问题事件

  • 创建了问题 今天

悬赏问题

  • ¥15 android studio导航组件根据导航图配置如何自动生成
  • ¥20 C#上传XML格式数据
  • ¥15 elementui上传结合oss接口断点续传,现在只差停止上传和继续上传,各大精英看下
  • ¥100 单片机hardfaulr
  • ¥20 手机截图相片分辨率降低一半
  • ¥50 求一段sql语句,遇到小难题了,可以50米解决
  • ¥15 速求,对多种商品的购买力优化问题(用遗传算法、枚举法、粒子群算法、模拟退火算法等方法求解)
  • ¥100 速求!商品购买力最优化问题(用遗传算法求解,给出python代码)
  • ¥15 虚拟机检测,可以是封装好的DLL,可付费
  • ¥15 kafka无法正常启动(只启动了一瞬间会然后挂了)