大文件分片下载
上一篇文章写到大文件上传,当大文件从后端传到前端时,如果是普通的文件流接口下载时,浏览器默认解析,文件大的情况,浏览器的内存会爆掉,这就是使用大文件分片下载的意义,需要和后端配合解析.
整体思路
1.需要先将整个文件的打包文件大小向后端获取.
2.再将文件的字节大小分割,再次向接口获取对应的字节(切记顺序不能错乱)
3.前端将文件拼接并下载.
页面代码
<template>
<div>
<!--面包屑导航-->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>大文件上传和下载</el-breadcrumb-item>
<el-breadcrumb-item>大文件下载</el-breadcrumb-item>
</el-breadcrumb>
<!--卡片视图-->
<el-card>
<el-table
:data="list"
border
style="width: 100%">
<el-table-column
fixed
prop="fileName"
label="文件名称">
</el-table-column>
<el-table-column
fixed="right"
label="操作">
<template slot-scope="scope">
<el-progress v-if="showPercent" :text-inside="true" :stroke-width="15" :percentage="percent"></el-progress>
<el-button v-else @click="handleDownload(scope.row)" type="text" size="small">下载</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
获取文件大小
handleDownload(row) {
// 获取文件大小进行分片 FILE_ZIP_DATA = [{
// fileName: '大文件',
// fileSize: 100 * 1024 * 1024
// }]
this.showPercent = true;
this.$axios.post('getZipFile', {...row}).then(res => {
// 静态的 接口文件FILE_ZIP_DATA
res = FILE_ZIP_DATA
this.file.size = res.size;
this.file.fileName = res.fileName;
const total = Math.ceil(res.size / this.size);
this.downloadFile(total)
}).catch(() => {
this.showPercent = false;
this.percent = 0;
})
},
切割文件并下载合并
// 下载文件
downloadFile(total) {
let len = total;
const list = [];
let fileArr = [];
let percentNum = 1;
for (let i = 0, len = total; i < len; i++) {
list.push({
fileName: this.file.fileName,
// 切片范围,判断最后一次切片和最后一次切片的文件大小
bytes: `bytes = ${i * this.size}-${i + 1 >= total ? this.fize.size : (i + 1) * this.size}`
})
}
// 将文件的分隔后分片接口下载并push到新的数组中,并且按照顺序(分片的顺序,切记不能错乱)
list.forEach((val, idx) => {
this.$axios.post('exportFile', val, {
headers: {
// 设置取部分的字节
Range: val.bytes
},
// 将接口设置成blob格式
responseType: 'blob'
}).then(res => {
len--;
if (res) {
// 进度条
this.percent = Math.floor(percentNum / total * 100);
fileArr[idx] = res;
percentNum++;
if (len === 0) {
this.saveFileDownload(arr);
}
}
})
})
},
// 合并文件保存
saveFileDownload(fileArr) {
const saveFile = new Blob(fileArr);
saveAs(saveFile, '大文件的名字');
setTimeout(() => {
this.showPercent = false;
this.percent = 0;
})
},
完整代码
<template>
<div>
<!--面包屑导航-->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>大文件上传和下载</el-breadcrumb-item>
<el-breadcrumb-item>大文件下载</el-breadcrumb-item>
</el-breadcrumb>
<!--卡片视图-->
<el-card>
<el-table
:data="list"
border
style="width: 100%">
<el-table-column
fixed
prop="fileName"
label="文件名称">
</el-table-column>
<el-table-column
fixed="right"
label="操作">
<template slot-scope="scope">
<el-progress v-if="showPercent" :text-inside="true" :stroke-width="15" :percentage="percent"></el-progress>
<el-button v-else @click="handleDownload(scope.row)" type="text" size="small">下载</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
import {FILE_ZIP_DATA} from "@/mock/data";
export default {
name: 'Params',
data() {
return {
list: [],
columns: [],
showPercent: false,
// 进度条
percent: 0,
size: 50 * 1024 * 1024,
file: {
fileName: "",
size: 0,
dataStream: []
}
}
},
// categories
mounted() {
this.init();
},
methods: {
// 初始化
init() {
this.getData();
},
handleDownload(row) {
// 获取文件大小进行分片 FILE_ZIP_DATA = [{
// fileName: '大文件',
// fileSize: 100 * 1024 * 1024
// }]
this.showPercent = true;
this.$axios.post('getZipFile', {...row}).then(res => {
// 静态的 接口文件FILE_ZIP_DATA
res = FILE_ZIP_DATA
this.file.size = res.size;
this.file.fileName = res.fileName;
const total = Math.ceil(res.size / this.size);
this.downloadFile(total)
}).catch(() => {
this.showPercent = false;
this.percent = 0;
})
},
// 下载文件
downloadFile(total) {
let len = total;
const list = [];
let fileArr = [];
let percentNum = 1;
for (let i = 0, len = total; i < len; i++) {
list.push({
fileName: this.file.fileName,
// 切片范围,判断最后一次切片和最后一次切片的文件大小
bytes: `bytes = ${i * this.size}-${i + 1 >= total ? this.fize.size : (i + 1) * this.size}`
})
}
// 将文件的分隔后分片接口下载并push到新的数组中,并且按照顺序(分片的顺序,切记不能错乱)
list.forEach((val, idx) => {
this.$axios.post('exportFile', val, {
headers: {
// 设置取部分的字节
Range: val.bytes
},
// 将接口设置成blob格式
responseType: 'blob'
}).then(res => {
len--;
if (res) {
// 进度条
this.percent = Math.floor(percentNum / total * 100);
fileArr[idx] = res;
percentNum++;
if (len === 0) {
this.saveFileDownload(arr);
}
}
})
})
},
// 合并文件保存
saveFileDownload(fileArr) {
const saveFile = new Blob(fileArr);
saveAs(saveFile, '大文件的名字');
setTimeout(() => {
this.showPercent = false;
this.percent = 0;
})
},
getData() {
// 调用接口
this.list = [{
id: 1,
fileName: '大文件'
}]
}
}
}
</script>
<style lang="less" scoped>
.cat_opt {
margin: 15px 0;
width: 200px;
}
</style>