前言
之前已经出过 大文件分片下载 的教程,期间也收到很多小伙伴的疑问说是功能上有点问题,也抽时间将一些大的问题修改了,验证了很多次,应该不会有什么问题了;在下载方案中涉及到断点续传部分的没有细讲,因为当时时间有限,所以只是稍微带过了,最近突然又闲下来了, 所以还是抽点时间将之前的方案细节更新完整
直接开始整
这里只涉及到续传功能的修改,要了解分片下载 请移步 前端大文件分片下载解决方案,没用你来砍我
修改工具类download.js
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
|
const progress_file_prefix = "progress_file_"
export async function getDownloadList(page, user, callback) { const baseDataBase = createInstance(baseDataBaseName) await baseDataBase.keys().then(async function (keys) { 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
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
| 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里的方法加载下载数据
1 2 3 4 5
| main.js
import store from './store'
store.dispatch("loadProgressList")
|
创建组件DownloadProgress.vue
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
| <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: {
retry(row) { this.start(row) },
start(row) { this.fileStatus[row.dataInstance] = { type: 'continue', progress: row.progress } downloadByBlock(row.fileName, row.url, row.dataInstance, this.fileStatus[row.dataInstance]) },
stop(row) { this.$set(this.fileStatus[row.dataInstance], "type", "stop") },
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 },
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 调用
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
| <template> <div id="app"> <el-button @click="download">下载文件</el-button> <DownloadProgress ref="downloadProgress"></DownloadProgress> </div> </template>
<script>
import DownloadProgress from "@/components/DownloadProgress" export default { name: 'App', components: { DownloadProgress }, methods: { download() { const dataBaseName = "progress_file_1" const parent = this.getAppVue(this) parent.$refs.downloadProgress.downloadFile("Subnautica.v63112.part01.rar", "/api/downloadByBlock", dataBaseName) },
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>
|
效果展示
- 新增下载
- 暂停下载
- 继续下载
- 删除下载
- 页面刷新不影响下载

最后
直接拿去整吧