使用Koa2实现服务端的文件上传下载
jsconst Koa = require("koa")
const router = require("koa-router")();
const cors = require("koa2-cors")
const fs = require('fs');
const path = require('path');
const {koaBody} = require('koa-body');
console.log(koaBody);
const {saveFile,getFileStream,readFile} = require("./utils/file")
const app = new Koa()
/** 文件上传 */
router.post("/upload",function(ctx,next){
const file = ctx.request.files.file;
const uniqueFilename = saveFile(file);
ctx.body = { filename: uniqueFileName };
})
/** 文件下载 */
router.get("/download/:filename",async function(ctx,next){
const filename = ctx.params.filename;
//request里面切出标识符字符串
let requestUrl = ctx.request.originalUrl;
//获取资源文件的绝对路径
let filePath = path.resolve(__dirname + "/uploads/" + decodeURI(filename));
let resHred = readFile(ctx.headers.range, filePath);
ctx.status = resHred.code
ctx.set(resHred.head);
ctx.set('Content-Disposition', `attachment; filename=${encodeURIComponent(filename)}`);
ctx.set('Content-Type', 'application/octet-stream');
let stream = fs.createReadStream(filePath, resHred.code == 200 ? {} : { start: resHred.start, end: resHred.end });
stream.pipe(ctx.res);
// //也可使用这种方式。
// stream.on('data', e => ctx.res.write(e));
// // 接收完毕
// stream.on('end', e => ctx.res.end());
ctx.respond = false;
return
})
router.post("/index",function(ctx,next){
ctx.body = {
data:"token....",
message:"获取token成功"
}
})
// app.use(async (ctx) => {
// ctx.body = "token....."
// })
// app.use(
// cors({
// origin: function(ctx){
// return "*" //解决跨域
// },
// maxAge:5,
// credentials:true,//是否携带cookie
// allowMethods:["GET","POST","PUT","DELETE"]
// })
// )
// koa-body 中间件来解析包含文件的 multipart/form-data 表单数据
app.use(koaBody({ multipart: true }));
app.use(router.routes())
//app.use(router.allowedMethods())
app.listen(3000)
关于文件操作的工具函数file.js
jsconst fs = require('fs');
const path = require('path');
function saveFile(file) {
const reader = fs.createReadStream(file.path);
const fileExtension = path.extname(file.name);
const uniqueFileName = `${Date.now()}${fileExtension}`;
const writer = fs.createWriteStream(path.join(__dirname, 'uploads', uniqueFileName));
reader.pipe(writer);
return uniqueFileName;
}
function getFileStream(filename) {
return fs.createReadStream(path.join(__dirname, '../uploads', filename));
}
/**
* [读文件]
* @param {String} range [数据起始位]
* @param {String} filePath [文件路径]
* @param {Number} chunkSize [每次请求碎片大小 (900kb 左右)]
*/
function readFile(range, filePath, chunkSize = 499999 * 2) {
//mime类型
const mime = {
"css": "text/css",
"gif": "image/gif",
"html": "text/html",
"ico": "image/x-icon",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"js": "text/javascript",
"json": "application/json",
"pdf": "application/pdf",
"png": "image/png",
"svg": "image/svg+xml",
"swf": "application/x-shockwave-flash",
"tiff": "image/tiff",
"txt": "text/plain",
"mp3": "audio/mp3",
"wav": "audio/x-wav",
"wma": "audio/x-ms-wma",
"wmv": "video/x-ms-wmv",
"xml": "text/xml",
"mp4": "video/mp4"
};
// 获取后缀名
let ext = path.extname(filePath);
ext = ext ? ext.slice(1) : 'unknown';
//未知的类型一律用"text/plain"类型
let contentType = mime[ext.toLowerCase()];
//建立流对象,读文件
let stat = fs.statSync(filePath)
let fileSize = stat.size;
let head = {
code: 200,
head: {
'Content-Length': fileSize,
'content-type': contentType,
}
};
console.log("range: ",range);
if (range) {
// 大文件分片
let parts = range.replace(/bytes=/, "").split("-");
let start = parseInt(parts[0], 10);
let end = parts[1] ? parseInt(parts[1], 10) : start + chunkSize;
end = end > fileSize - 1 ? fileSize - 1 : end;
chunkSize = (end - start) + 1;
head = {
code: 206,
filePath,
start,
end,
head: {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'content-type': contentType,
'Content-Length': chunkSize,
'Accept-Ranges': 'bytes'
}
}
}
return head;
}
module.exports = {
saveFile,
getFileStream,
readFile
}


本文作者:千寻
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!