-
该内容稍微改动原博客代码
原博客链接 -
前端代码:
<template>
<div>
<div style="margin: 20px">
<h2>Upload Large Files</h2>
<br />
<el-row>
<el-col :span="4">
<input
id="uppic"
type="file"
multiple
@change="selectedFiles()"
ref="upload"
/>
<label for="uppic">
<div id="wrapper">
<div id="cell">
<span>
<i class="el-icon-folder-opened"></i>
Choose Files
</span>
</div>
</div>
</label>
</el-col>
<el-col :span="10" :offset="1">
<el-button
id="userbtn"
class="bg-main tc userbtn"
type="success"
@click="upload"
>
<i class="el-icon-upload"></i>
Upload
</el-button>
</el-col>
</el-row>
<el-row v-if="fileList.length != 0">
<el-table :data="fileList" style="width: 100%">
<el-table-column prop="name" label="Name" width="500">
</el-table-column>
<el-table-column prop="size" label="Size (KB)" width="180">
</el-table-column>
</el-table>
</el-row>
<el-row>
<el-col :span="20">
<el-progress
:text-inside="true"
:stroke-width="26"
:percentage="percentage"
></el-progress>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import async from "async";
export default {
data: () => ({
percentage: 0,
fileList: [],
}),
methods: {
upload() {
this.percentage = 0;
let _this = this;
// console.log(this.$refs.upload.files);
for (let k in this.$refs.upload.files) {
if (k == "0") {
let file = this.$refs.upload.files[k], //上传文件主体
name = file.name, //文件名
size = file.size, //总大小
succeed = 0; //当前上传数
console.log(file);
let shardSize = 2 * 1024 * 1024, //以2MB为一个分片
shardCount = Math.ceil(size / shardSize); //总片数
// console.log(shardSize);
/*生成上传分片文件顺充,通过async.eachLimit()进行同步上传
attr里面是[0,1,2,3...,最后一位]
*/
let attr = [];
for (let i = 0; i < shardCount; ++i) {
attr.push(i);
}
console.log(attr);
async.eachLimit(
attr, //原始数据数组
1, //每次并行处理的数据量
async function (item, callback) {//数据进行的处理 item 为原始数据数组的元素
// console.log(item);
let i = item;
let start = i * shardSize, //当前分片开始下标
end = Math.min(size, start + shardSize); //结束下标
//构造一个表单,FormData是HTML5新增的
let form = new FormData();
form.append("data", file.slice(start, end)); //slice方法用于切出文件的一部分
form.append("name", name); //文件名字
form.append("total", shardCount); //总片数
form.append("index", i + 1); //当前片数'
await this.http.post("接口地址",form,{
timeout: 120*1000
})
.then(res=>{
++succeed;
/*返回code为0是成功上传,1是请继续上传*/
if(res.data.code==0){
console.log(res.data.mssg);
_this.percentage=0
_this.fileList.shift()
_this.$message({
type:'success',
message:'upload finish'+file.name,
showClose:true
})
}else if(res.data.code==1){
console.log(res.data.msg);
}
//生成当前进度百分比
_this.percentage=Math.round(succeed/shardCount*100);
callback()
})
},
function (err) {
// console.log('上传成功');
}
);
}
}
},
selectedFiles() {
// console.log('selected',this.$refs.upload.files)
this.fileList = [...this.$refs.upload.files];
},
},
watch: {},
};
</script>
<style scoped>
#uppic {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.el-row {
margin-bottom: 20px;
}
label {
color: aquamarine;
background-color: #796e02e3;
height: 40px;
display: block;
text-align: center;
}
#wrapper {
display: table;
height: 40px;
margin: 0 auto;
color: rgb(255, 255, 255);
}
#cell {
display: table-cell;
vertical-align: middle;
}
</style>
node端代码:
- 引入并调用user方法:
const upload= require("../middleware/upload.js");
router.post("/fpsc", async (req, res) => {
upload(req,res)
})
- upload方法
function upload(req, res, config){
let fs = require('fs');
let async = require('async');//异步模块
const uuid = require('node-uuid');
let formidable = require('formidable')
let form = new formidable.IncomingForm();
//设置编辑
form.encoding = 'utf-8';
let dirPath = __dirname + "/../uploadFiles/tep/";
//设置文件存储路径
form.uploadDir = dirPath;
//设置单文件大小限制
// form.maxFilesSize = 200 * 1024 * 1024;
/*form.parse表单解析函数,fields是生成数组用获传过参数,files是bolb文件名称和路径*/
form.parse(req, function (err, fields, files) {
files = files['data'];//获取bolb文件
let index = fields['index'];//当前片数
let total = fields['total'];//总片数
let name = fields['name'];//文件名称
let url = dirPath + '/' + name.split('.')[0] + '_' + index + '.' + name.split('.')[1];//临时bolb文件新名字
fs.renameSync(files.path, url);//修改临时文件名字
try {
if (index == total) {//当最后一个分片上传成功,进行合并
/*
检查文件是存在,如果存在,重新设置名称
*/
let uid = uuid.v4()
fs.mkdirSync(__dirname + "/../uploadFiles/" + uid)
let pathname = __dirname + "/../uploadFiles/" + uid + '/' + name;//上传文件存放位置和名称
fs.access(pathname, fs.F_OK, (err) => {
if (!err) {
let myDate = Date.now();
pathname = dirPath + '/' + myDate + name;
console.log(pathname);
}
});
//这里定时,是做异步串行,等上执行完后,再执行下面
setTimeout(function () {
/*进行合并文件,先创建可写流,再把所有BOLB文件读出来,
流入可写流,生成文件
fs.createWriteStream创建可写流
aname是存放所有生成bolb文件路径数组:
['Uploads/img/3G.rar1','Uploads/img/3G.rar2',...]
*/
let writeStream = fs.createWriteStream(pathname);
// console.log(writeStream);
let aname = [];
for (let i = 1; i <= total; i++) {
let url = dirPath + '/' + name.split('.')[0] + '_' + i + '.' + name.split('.')[1];
aname.push(url);
}
//async.eachLimit进行同步处理
async.eachLimit(aname, 1, function (item, callback) {
//item 当前路径, callback为回调函数
fs.readFile(item, function (err, data) {
if (err) throw err;
//把数据写入流里
console.log(data);
writeStream.write(data);
//删除生成临时bolb文件
fs.unlink(item, function () { console.log('删除成功'); })
callback();
});
}, function (err) {
if (err) throw err;
//后面文件写完,关闭可写流文件,文件已经成生完成
writeStream.end();
//返回给客服端,上传成功
let data = JSON.stringify({
'code': 0, "data": {
"source_store_id": uid,
"file_name": name
}
});
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
res.end(data);//返回数据
});
}, 50);
} else {//还没有上传文件,请继续上传
let data = JSON.stringify({ 'code': 1, 'msg': '继续上传' });
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
res.end(data);//返回数据
}
} catch (err) {
console.log(err)
}
});
}
module.exports = upload;