相关往期文章:
文章一:Node框架之Egg —— EggJS+MySQL(mysql2+egg-sequelize+egg-cors)实现简单的增删改查
本实例源码:
- gitee:gitee.com/vince-lee/e…
- github:github.com/vince-lee92…
一、简介
1. 本例实现效果
上传文件到本地,按照创建日期文件夹去管理文件;即将同一天的上传文件放入到当天日期的文件夹下;如下图所示:
2. 所用到的第三方插件:
moment
:用于创建以日期划分的文件夹;
uuid
:用于给上传的文件重新命名。
安装:npm install moment uuid --save
3. 实现方法
上传文件总共有两种实现方法:
- file形式上传
- stream形式上传
二、代码实现
1. file形式上传
- 配置
// config/config.default.js 'use strict'; module.exports = appInfo => { const config = exports = {}; config.keys = appInfo.name + '_1637216035577_8221'; config.middleware = []; /* 取消安全证书验证 */ config.security = { csrf: { enable: false, }, domainWhiteList: ['*'], // 白名单 }; config.multipart = { mode: 'file', fileSize: '50mb', fileExtensions: ['.png', '.jpg'], // 扩展几种上传的文件格式 }; return { ...config }; };
- 上传代码
// app/controller/uploadForFile.js 'use strict'; const fs = require('fs') const path = require('path') const Controller = require('egg').Controller; class UploadForFileController extends Controller { async upload() { const { ctx } = this const file = ctx.request.files[0] const fileData = fs.readFileSync(file.filepath); const base64Str = Buffer.from(fileData, 'binary').toString('base64'); const bufferData = Buffer.from(base64Str, 'base64'); // 获取当前日期,用于文件夹创建 const dirName = ctx.helper.formatTime(new Date()) // 指定上传路径 const uploadBasePath = '../public/uploadForFile' // 文件重命名 const filename = `${ctx.helper.getUUID()}${path.extname(file.filename)}` const dir = path.join(__dirname, uploadBasePath, dirName); const src = path.join(__dirname, uploadBasePath, dirName, filename); // 判断是否存在该文件夹,不存在则创建。 if (!fs.existsSync(dir)) fs.mkdirSync(dir); try { await fs.writeFileSync(src, bufferData); ctx.status = 200; ctx.body = { filename, url: `/public/uploadForFile/${dirName}/${filename}` }; } catch (e) { ctx.status = 500; ctx.body = { msg: '上传文件失败' }; } } } module.exports = UploadForFileController;
2. stream形式上传
- 配置:
// config/config.default.js 'use strict'; module.exports = appInfo => { const config = exports = {}; config.keys = appInfo.name + '_1637216035577_8221'; config.middleware = []; /* 取消安全证书验证 */ config.security = { csrf: { enable: false, }, domainWhiteList: ['*'], // 白名单 }; config.multipart = { mode: 'stream', fileSize: '50mb', fileExtensions: ['.png', '.jpg'], // 扩展几种上传的文件格式(白名单) }; return { ...config }; };
- 上传代码
// app/controller/uploadForStream.js 'use strict'; const fs = require('fs'); const path = require('path'); const sendToWormhole = require('stream-wormhole'); const Controller = require('egg').Controller; class UploadForStreamController extends Controller { async upload() { const { ctx } = this; const stream = await ctx.getFileStream(); // 获取当前日期,用于文件夹创建 const dirName = ctx.helper.formatTime(new Date()) // 指定上传路径 const uploadBasePath = '../public/uploadForStream' // 文件重命名 const filename = `${ctx.helper.getUUID()}${path.extname(stream.filename)}` const dir = path.join(__dirname, uploadBasePath, dirName); const src = path.join(__dirname, uploadBasePath, dirName, filename); // 判断是否存在该文件夹,不存在则创建。 if (!fs.existsSync(dir)) fs.mkdirSync(dir); const result = await new Promise((resolve, reject) => { const remoteFileStream = fs.createWriteStream(src); stream.pipe(remoteFileStream); let errFlag; remoteFileStream.on('error', err => { errFlag = true; sendToWormhole(stream); remoteFileStream.destroy(); reject(err); }); remoteFileStream.on('finish', async () => { if (errFlag) return; resolve(filename); }); }); ctx.body = { filename, url: `/public/uploadForStream/${dirName}/${result}` }; } } module.exports = UploadForStreamController;
三、 总结
本实例中,通过两种方法上传文件到本地:file形式上传
和stream形式上传
。
值得注意的是:
1. 配置与获取方式
两种方式配置和获取前端传过来的文件参数的方式不一样。
- file形式上传
// 配置 config.multipart = { mode: 'file', fileSize: '50mb', whitelist: [ '.xlsx' ], }; // 获取 const files = ctx.request.files;
- stream形式上传
// 配置 config.multipart = { mode: 'stream', fileSize: '50mb', fileExtensions: ['.png', '.jpg'], // 扩展几种上传的文件格式(白名单) }; // 获取 const stream = await ctx.getFileStream();
2. 扩展上传的文件格式(白名单)
在配置中,还得指定好上传文件的类型,你才会上传成功,不然你就会报错:
但是,在上传图片(png / jpg / jpeg / gif)的时候,是不用添加白名单的。
3. moment、uuid
插件的使用
在本示例中,我将这两个插件封装在helper中的,这两个插件取决于你的项目是否使用,我自己喜欢用这种方式去管理文件和重命名文件名。
// app/extend/helper.js
const moment = require('moment')
const { v4: uuidv4 } = require('uuid');
module.exports = {
formatTime(date, format = 'YYYY-MM-DD') {
return moment(date).format(format)
},
getUUID() {
return uuidv4();
},
};