当前位置:首页 » 《我的小黑屋》 » 正文

【后续 断点续传】前端大文件分片下载解决方案,没用你来砍我

6 人参与  2024年12月18日 16:01  分类 : 《我的小黑屋》  评论

点击全文阅读


前言

之前已经出过 大文件分片下载 的教程,期间也收到很多小伙伴的疑问说是功能上有点问题,也抽时间将一些大的问题修改了,验证了很多次,应该不会有什么问题了;在下载方案中涉及到断点续传部分的没有细讲,因为当时时间有限,所以只是稍微带过了,最近突然又闲下来了, 所以还是抽点时间将之前的方案细节更新完整

直接开始整

这里只涉及到续传功能的修改,要了解分片下载 请移步 前端大文件分片下载解决方案,没用你来砍我

修改工具类download.js

// 添加获取下载列表的方法// 定义文件存储数据库名的前缀const progress_file_prefix = "progress_file_"/** * @description 获取下载列表 * @param {*} page 页面 * @param {*} user 用户 * @param {*} callback 返回 */export async function getDownloadList(page, user, callback) {    // 取得基础数据库    const baseDataBase = createInstance(baseDataBaseName)    await baseDataBase.keys().then(async function (keys) {        // 包含所有key 名的数据        let fileList = []        for (let i = 0; i < keys.length; i++) {            if (keys[i].indexOf(progress_file_prefix) > -1) {                // 获取数据                await getData(keys[i], baseDataBase, async (res) => {                    // 存储文件名和对应的数据库实例名                    fileList.push(                        { fileName: res, dataInstance: keys[i] }                    )                })            }        }        // 获取下载进度        for (let i = 0; i < fileList.length; i++) {            // 获取数据库实例            const dataBase = createInstance(fileList[i].dataInstance)            // 获取数据            await getData(progressKey, dataBase, async (progress) => {                if (progress) {                    // 赋值进度及状态                    fileList[i].fileSize = progress.fileSize                    fileList[i].progress = progress.progress ? progress.progress : 0                    fileList[i].status = progress.status ? progress.status : "stopped"                    fileList[i].url = progress.url                }            })        }        callback(fileList)    }).catch(function (err) {        callback(err)    })}

添加 store/index.js

store/inde.jsimport Vue from 'vue'import Vuex from 'vuex'import { getDownloadList } from "@/utils/download.js"Vue.use(Vuex)export default new Vuex.Store({    state: {        // 下载进度列表        progressList: []    },    mutations: {        setProgress: (state, progressObj) => {            // 如果进度表有数据            if (state.progressList.length) {                const obj = state.progressList.find(item => item.dataInstance == progressObj.dataInstance)                if (obj) {                    if (progressObj.progress) {                        // 改变当前进度对象的进度                        obj.progress = progressObj.progress                    }                    if (progressObj.status) {                        // 改变当前进度对象的状态                        obj.status = progressObj.status                    }                } else {                    // 添加新的进度                    state.progressList.push(progressObj)                }            } else {                // 添加新的进度                state.progressList.push(progressObj)            }        },        delProgress: (state, props) => {            // 删除进度对象            state.progressList.splice(state.progressList.findIndex(item => item.dataInstance == props), 1)        }    },    actions: {        // 从数据库中加载进度数据        loadProgressList({ commit, state }) {            return new Promise(resolve => {                getDownloadList("", state.username, function (res) {                    state.progressList = res                    resolve()                })            })        }    }})

在main.js里调用 store里的方法加载下载数据

main.jsimport store from './store'// 加载下载进度store.dispatch("loadProgressList")

创建组件DownloadProgress.vue

<template>    <div class="download-container">        <div class="download">            <div @click="btnDownload">                <i class="el-icon-download"></i>                <div class="text">下载</div>            </div>        </div>        <el-dialog class="dialog-form" title="下载列表" :visible.sync="downloadDialog" :close-on-click-modal="false"            :show-close="false" append-to-body width="60%">            <div style="padding:16px">                <el-table ref="table" :data="$store.state.progressList" :header-cell-style="{ backgroundColor: '#f8f8f8' }">                    <el-table-column prop="fileName" label="文件名"></el-table-column>                    <el-table-column prop="status" label="下载状态">                        <template #default="scope">                            <el-tag v-if="scope.row.status == 'downloading'">Downloading</el-tag>                            <el-tag v-if="scope.row.status == 'success'" type="success">Success</el-tag>                            <el-tag v-if="scope.row.status == 'error'" type="danger">Download Failed</el-tag>                            <el-tag v-if="scope.row.status == 'stopped'">Stopped</el-tag>                        </template>                    </el-table-column>                    <el-table-column prop="progress" label="下载进度">                        <template #default="scope">                            <el-progress :stroke-width="12" :percentage="scope.row.progress"></el-progress>                        </template>                    </el-table-column>                    <el-table-column label="操作">                        <template #default="scope">                            <template v-if="scope.row.status == 'stopped'">                                <el-button title="开始" circle icon="el-icon-video-play"                                    @click="start(scope.row)"></el-button>                                <el-button title="结束" circle icon="el-icon-delete" @click="del(scope.row)"></el-button>                            </template>                            <template v-else-if="scope.row.status == 'downloading'">                                <el-button title="暂停" circle icon="el-icon-video-pause"                                    @click="stop(scope.row)"></el-button>                                <el-button title="结束" circle icon="el-icon-delete" @click="del(scope.row)"></el-button>                            </template>                            <template v-else-if="scope.row.status == 'error'">                                <el-button title="重试" circle icon="el-icon-refresh" @click="retry(scope.row)"></el-button>                                <el-button title="结束" circle icon="el-icon-delete" @click="del(scope.row)"></el-button>                            </template>                        </template>                    </el-table-column>                </el-table>            </div>            <div class="dialog-operate-box">                <el-button size="mini" @click="downloadDialog = false">取消</el-button>            </div>        </el-dialog>    </div></template><script>import { downloadByBlock } from '@/utils/download.js'export default {    data() {        return {            downloadDialog: false,            // 记录每个下载文件            fileStatus: {}        }    },    watch: {        // 监听下载列表的变化        "$store.state.progressList": function () {            this.$nextTick(() => {                const progressList = this.$store.state.progressList                progressList.forEach(item => {                    // 获取之前下载状态 还原操作                    const status = sessionStorage.getItem(item.dataInstance)                    if (status == 'downloading' && item.status != status) {                        // 如果是下载中的  就继续,这里是防止手动刷新页面后把正在下载中的任务暂停了                        this.start(item)                    }                })            })        }    },    methods: {        /**         * 重试         * @param {*} row          */        retry(row) {            this.start(row)        },        /**         * 开始下载         * @param {*} row          */        start(row) {            // 记录文件的下载状态 方便后续的操作            this.fileStatus[row.dataInstance] = {                type: 'continue',                progress: row.progress            }            downloadByBlock(row.fileName, row.url, row.dataInstance, this.fileStatus[row.dataInstance])        },        /**         * 暂停下载         * @param {*} row          */        stop(row) {            this.$set(this.fileStatus[row.dataInstance], "type", "stop")        },        /**         * 删除下载         * @param {*} row          */        del(row) {            if (this.fileStatus[row.dataInstance] && row.status != "stopped") {                this.$set(this.fileStatus[row.dataInstance], "type", "cancel")            } else {                this.fileStatus[row.dataInstance] = { type: "cancel" }                downloadByBlock(row.fileName, row.url, row.dataInstance, this.fileStatus[row.dataInstance])            }        },        /**         * 打开下载列表         */        btnDownload() {            this.downloadDialog = true        },        /**         * 下载文件         * @param {*} fileName          * @param {*} url          * @param {*} dataBaseName          */        downloadFile(fileName, url, dataBaseName) {            this.fileStatus[dataBaseName] = { type: null }            downloadByBlock(fileName, url, dataBaseName, this.fileStatus[dataBaseName])            this.btnDownload()        }    }}</script><style scoped>.download-container {    position: fixed;    right: 0px;    bottom: 60px;    z-index: 2041;}.download i {    font-size: 18px}.download>div {    display: flex;    flex-direction: column;    justify-content: center;    align-items: center;    font-size: 14px;    padding: 12px;    border-radius: 4px;    color: #fff;    margin: 16px 2px 16px 0;    cursor: pointer;}.download text {    padding-top: 10px;    word-break: break-all;}.download {    background: #9a4b9b;}</style>

App.vue 调用

<template>  <div id="app">    <el-button @click="download">下载文件</el-button>    <!--之所以放到APP里,是可以做成全局通用的,在任何一个页面都可以打开下载列表-->    <DownloadProgress ref="downloadProgress"></DownloadProgress>  </div></template><script>// 引入之前创建的组件import DownloadProgress from "@/components/DownloadProgress"export default {  name: 'App',  components: {    DownloadProgress  },  methods: {    download() {      // 这里的 dataBaseName 需要以 progress_file_ 开头(也可自定义,要与download.js 里的progress_file_prefix 值一致)      const dataBaseName = "progress_file_1"      const parent = this.getAppVue(this)      parent.$refs.downloadProgress.downloadFile("Subnautica.v63112.part01.rar", "/api/downloadByBlock", dataBaseName)    },    /**     * 递归寻找App里的 DownloadProgress组件     * @param {*} vue      */    getAppVue(vue) {      if (vue.$refs.downloadProgress) {        return vue      } else {        this.getAppVue(vue.$parent)      }    }  }}</script><style>#app {  font-family: 'Avenir', Helvetica, Arial, sans-serif;  -webkit-font-smoothing: antialiased;  -moz-osx-font-smoothing: grayscale;  text-align: center;  color: #2c3e50;  margin-top: 60px;}</style>

效果展示

新增下载暂停下载继续下载删除下载页面刷新不影响下载

在这里插入图片描述

最后

直接拿去整吧


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/203123.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1