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

news2024/11/16 3:21:12

前言

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

直接开始整

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

修改工具类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.js

import 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.js

import 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://www.coloradmin.cn/o/1984252.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】索引和事务

秋招 秋招中最经典&#xff0c;最高频的面试题 文章目录 索引 index操作索引的 SQL查看索引创建索引删除索引 事务操作四个核心特性 索引 index 在数据库中建立一个特殊的“目录“&#xff08;一系列特定的数据结构&#xff09;&#xff0c;为了加快查询速度 select 查询都是遍…

Go项目依赖:Tidy命令与离线依赖获取

引言 Go语言以其简洁性和高效性在开发者中广受欢迎&#xff0c;而Go的模块系统则为依赖管理提供了极大的便利。本文将介绍如何使用go mod tidy命令来整理项目的依赖关系&#xff0c;以及在无法在线获取依赖的情况下&#xff0c;如何通过离线方式获取并使用依赖。 使用go mod …

数论基础知识(下)

目录 欧拉函数 n的分解质因数求欧拉函数 试除法求欧拉函数值 积性函数 筛法 朴素筛 埃氏筛 欧拉筛 (线性筛) 线性筛欧拉函数 快速幂 同余 欧拉定理 费马小定理 乘法逆元 欧拉函数 互质 : ∀ a , b ∈ N &#xff0c;若 gcd ( a , b ) 1 &#xff0c;则 a…

Java零基础之多线程篇:线程控制

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

RISC-V竞赛|第二届 RISC-V 软件移植及优化锦标赛报名正式开始!

目录 赛事背景 赛道方向 适配夺旗赛 优化竞速赛 比赛赛题&#xff08;总奖金池8万元&#xff01;&#xff09; &#x1f525;竞速赛 - OceanBase 移植与优化 比赛赛程&#xff08;暂定&#xff09; 赛事说明 「赛事背景」 为了推动 RISC-V 软件生态更快地发展&#xff0…

健身动作AI识别,仰卧起坐计数(含UI界面)

用Python和Mediapipe打造&#xff0c;让你的运动效果一目了然&#xff01; 【技术揭秘】 利用Mediapipe的人体姿态估计&#xff0c;实时捕捉关键点&#xff0c;精确识别动作。 每一帧的关键点坐标和角度都被详细记录&#xff0c;为动作分析提供数据支持。 支持自定义动作训练&a…

Jave-this关键字

目录 1.this关键字 先看一段代码&#xff0c;并分析问题 什么是this 案例演示 2.this的注意事项和使用细节 1.this关键字 先看一段代码&#xff0c;并分析问题 什么是this this就 代表的是当前对象。 this可以大概理解成“我的” &#xff0c;比如我的书包&#xff0c;这…

基于Java中的SSM框架实现大创项目申报管理系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现大创项目申报管理系统演示 摘要 经济的高速发展进一步推动了种类繁多的项目落地&#xff0c;传统的项目在申报过程中主要通过纸质文件的方式实现申报信息的传递&#xff0c;同时不同的项目内容所对应的申报资料模板也有所差异&#xff0c;人工整理的提…

没有显卡,怎么玩AI绘图?

或许很多人跟我一样&#xff0c;没有显卡&#xff0c;但又很想玩AI绘图&#xff0c;但本地绘图怕是无缘了&#xff0c;只能借助云GPU的方式了。 今天跟大家分享一下一个简单目前可白嫖无门槛的方法实现无显卡也能玩AI绘图。 方案就是ComfyUIBizyAir云节点。 ComfyUI介绍 来…

【软考】虚拟存储器

目录 1. 说明2. 定义3. 工作原理4. 管理方式5. 优点6. 例题6.1 例题1 1. 说明 1.在概念上&#xff0c;可以将主存存储器看作一个由若干个字节构成的存储空间&#xff0c;每个字节(称为一个存储单元)有一个地址编号&#xff0c;主存单元的该地址称为物理地址(Physical Address)…

数值分析——三次样条插值

系列文章目录 数值分析——拉格朗日插值 数值分析——牛顿插值多项式 数值分析——埃尔米特&#xff08;Hermit&#xff09;插值 数值分析——分段低次插值 文章目录 系列文章目录前言一、理论推导1.三次样条函数2.三次样条插值函数的求解条件3.三次样条插值函数的建立 二、MA…

简单反射型XSS的复现

xss反射型攻击&#xff1a; 1.最简单的漏洞复现&#xff1a; 这里我们有一个最简单的网页&#xff1a;由于地址不存在&#xff0c;所以图片加载不出来。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta…

FP8量化

https://arxiv.org/html/2402.16363v5 LLama3.1用了FP8量化&#xff1a; FP8也可以用scaling factor来扩大表示范围&#xff0c;对吧&#xff1f;

开源在线剪切板 PrivateBin 安装和使用教程

我们经常需要在网上快速分享一些文本内容&#xff0c;比如代码片段、临时笔记或者敏感信息。传统的在线剪贴板服务虽然使用方便&#xff0c;但往往缺乏足够的隐私保护。 那么&#xff0c;有没有一种既方便又安全的在线文本分享方式呢&#xff1f;今天我要向大家推荐一个优秀的…

常见的图像融合方法

这里我们将介绍一些常用的图像融合方式&#xff0c;并不涉及到诸如CutMix、MixUp、TokenMix、Mosaic、Copy-Paste等图像增强方法。 首先是读取图像&#xff0c;我们这边采用了PIL库进行&#xff0c;那么读进来就应该是一个Image对象。下面介绍Image对象与array的转换方式。 f…

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测

土地利用/土地覆盖数据是生态、环境和气象等领域众多模型的重要输入参数之一。基于遥感影像解译&#xff0c;可获取历史或当前任何一个区域的土地利用/土地覆盖数据&#xff0c;用于评估区域的生态环境变化、评价重大生态工程建设成效等。借助CLUE模型&#xff0c;实现对未来土…

AI9-文本识别

本章主要介绍文本识别算法的理论知识,包括背景介绍、算法分类和部分经典论文思路。 通过本章的学习,你可以掌握: 1. 文本识别的目标 2. 文本识别算法的分类 3. 各类算法的典型思想 1 背景介绍 文本识别是OCR(Optical Character Recognition)的一个子任务,其任务为识别一个…

pytorch的入门使用

pytorch安装略&#xff01; 一.张量Tensor 张量是一个统称其中包含0阶&#xff0c;1阶&#xff0c;2阶&#xff0c;3阶&#xff0c;4阶&#xff0c;.......n阶。 0阶&#xff1a;标量&#xff0c;常数&#xff0c;0-D Tensor 1阶&#xff1a;向量&#xff0c;1-D Tensor 2…

使用java反编译工具jad

文章目录 反编译工具 JAD下载配置环境变量使用其他反编译工具 JD-GUI 反编译工具 JAD 反编译是指将编译后的字节码文件&#xff08;.class 文件&#xff09;转换回可读的 Java 源代码。JAD (Java Decompiler) 是一个经典的反编译工具&#xff0c;广泛用于将 Java 字节码反编译…

国内AI大模型168个,哪个最有前途?

168个国产大模型&#xff0c;都是什么来头&#xff1f; 1785年&#xff0c;瓦特改进了蒸汽机&#xff0c;人类从此摆脱了手工业的桎梏&#xff0c;迈向辉煌的蒸汽时代。 1870年&#xff0c;第二次工业革命光芒四溢&#xff0c;人类踏上了电气时代的漫长征程。 20世纪70年代后…