分段上传文件
后端koa+formidable
const fs = require("mz/fs")
const path = require("path")
const Koa = require("koa")
const app = new Koa()
const formidable = require("formidable")
const static = require("./lib/koa-static")
const uploadPath = path.resolve(__dirname, "./temps")
app.use(static(__dirname))
app.use(async (ctx, next) => {
if (ctx.path === "/upload" && ctx.method === "POST") {
let isExit = fs.existsSync(uploadPath)
//不存在上传文件夹则创建
if (!isExit) fs.mkdirSync(uploadPath)
let form = new formidable.IncomingForm()
form.uploadDir = uploadPath;
// 文件名不存在,则表示还没全部上传成功
let filename = await parser(form, ctx.req)
if (!filename) {
ctx.body = `模块${num}上传成功`
} else {
console.log("complete")
await mergeFiles()
ctx.body = "complete"
}
} else {
await next()
}
})
let num = 0
let total = 0
let filename = ""
async function parser(form, req) {
return await new Promise((resolve, rejects) => {
let filepath = null
form.parse(req, async (err, fields, files) => {
if (err) rejects(error)
filename = fields.filename
total = fields.count * 1
filepath = files.chunk.path
await fs.rename(filepath, path.join(uploadPath, num + ""))
})
form.on("end", async () => {
if (++num == total) {
// 如果全部模块上传完成,返回文件名,上传模块重置为0
num = 0
setTimeout(() => {
resolve(filename)
})
} else {
// 还没全部上传完
resolve(false)
}
})
})
}
// 合并上传的文件
async function mergeFiles() {
let dirs = await fs.readdir(uploadPath)
dirs = dirs.sort((a, b) => a - b).map((dir) => path.join(uploadPath, dir))
let ws = fs.createWriteStream(path.join(uploadPath, filename))
for (let i = 0; i < dirs.length; i++) {
ws.write(fs.readFileSync(dirs[i]))
}
ws.end()
ws.on("close", () => {
console.log("close")
})
}
app.use(async (ctx, next) => {
ctx.body = "Not Found"
})
app.listen(3000);
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>upload</title>
</head>
<body>
<div><input type="file" id="input"></div>
<button id="btn">submit</button>
<script>
let inputel = document.getElementById("input");
let submitBtn = document.getElementById("btn");
let index = 0;
// 每次只上传10mb
let size = 1024*1024*10;
let file,filename,totalSize,count;
inputel.addEventListener("change",(e)=>{
file = e.target.files[0];
filename = file.name;
totalSize = file.size; // 总大小
count = Math.ceil(totalSize/size); // 总上传次数
console.log("count",count);
});
function upload(){
let uploadSize = index*size;
let min = Math.min(size,totalSize-uploadSize);
if(min>=0){
let fd = new FormData();
fd.append("chunkIndex",index);
fd.append("count",count);
fd.append("filename",filename);
// slice() 可以将file对象转化成 Blob
// Blob 对象表示一个不可变、原始数据的类文件对象
fd.append("chunk",file.slice(uploadSize,uploadSize+min));
uploadFile(fd);
index++;
upload();
}
}
function uploadFile(fd){
let request = new XMLHttpRequest();
request.open("POST","/upload",true);
request.onload = function(){
console.log(request.response);
}
request.send(fd)
}
submitBtn.addEventListener("click",()=>{
upload();
})
</script>
</body>
</html>