先上egg-multipart的配置
config.multipart = {
mode: 'stream',
fileModeMatch: /^\/uploadFile$/,// uploadFile接口使用file模式,其他使用stream模式
tmpdir: path.join(os.tmpdir(), 'egg-multipart-tmp', appInfo.name),
cleanSchedule: {
// run tmpdir clean job on every day 04:30 am
cron: '0 30 4 * * *',
},
fileSize: '50mb',// 文件大小限制-string, 错误:400 Bad request
whitelist,// 文件类型-白名单-array, 错误:400 Bad request
};
1.前端使用Antd的upload组件,文章末尾附带此组件前后端交互方法
file模式:
1.从request中取得文件的filepath和filename
2.使用fs的readFileSync读取文件
3.使用Buffer将文件转成base64的格式
4.判断是否有目标文件夹,没有就创建
5.判断此文件是否存在,如果存在,删除
6.writeFileSync写入文件
async addHead({ request }) {
const { ctx } = this;
const file = request.files[0];
const data = fs.readFileSync(file.filepath);
const base64str = Buffer.from(data, 'binary').toString('base64');
const bufferData = Buffer.from(base64str, 'base64');
const uplaodBasePath = '../../app/public/upload/';
const dirName = ctx.user.userName + 'HeadImg';
const dir = path.join(__dirname, uplaodBasePath, dirName);
const src = path.join(__dirname, uplaodBasePath, dirName, file.filename);
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
const res = fs.readdirSync(dir);
if (res.length > 0) fs.unlinkSync(path.join(__dirname, uplaodBasePath, dirName, res[0]));
try {
await fs.writeFileSync(src, bufferData);
ctx.body = { name: file.filename };
ctx.status = 200;
this.operationLogger(request, '上传、更新用户头像', true);
} catch (e) {
ctx.body = { msg: '写入图片失败' };
ctx.status = 400;
this.serverError('上传、更新用户头像失败', request, '上传、更新用户头像');
}
}
stream模式
1. 创建流
2.获取文件名
3.判断目标文件夹,没有就创建
4.判断文件是否存在,如果存在,删除 or 返回错误信息
5.数量限制
6.创建目标文件
7.通过管道写入流
async add({ request }) {
const { ctx } = this;
const stream = await ctx.getFileStream();
const filename = stream.filename;
// 上传基础目录
const uplaodBasePath = '../../app/public/upload/';
// 生成文件夹
const dirName = ctx.user.userName;
const dir = path.join(__dirname, uplaodBasePath, dirName);
const dirImg = path.join(__dirname, uplaodBasePath, dirName, filename);
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
if (fs.existsSync(dirImg)) {
this.operationLogger(request, '图片上传', false);
ctx.body = { msg: '此图片名已存在' };
ctx.status = 400;
return;
}
const res = fs.readdirSync(dir);
if (res.length > 2) {
ctx.body = { msg: '最多只能传三张图片' };
ctx.status = 400;
return;
}
const target = path.join(__dirname, uplaodBasePath, dirName, filename);
// 写入流
const writeStream = fs.createWriteStream(target);
try {
// 写入文件
await awaitWriteStream(stream.pipe(writeStream));
this.operationLogger(request, '图片上传', true);
ctx.body = { name: filename };
ctx.status = 200;
} catch (err) {
// 必须将上传的文件流消费掉,要不然浏览器响应会卡死
await sendToWormhole(stream);
this.operationLogger(request, '图片上传', false);
ctx.body = { msg: '写入图片失败' };
ctx.status = 400;
throw err;
}
}
以下是和前端的交互,针对的是antd的upload组件
首先,注意上面的返回结构:
正确的时候:
ctx.body = { name: filename }; ctx.status = 200;
错误的时候:
ctx.body = { msg: '最多只能传三张图片' }; ctx.status = 400;
upload组件判断是否成功是同过http状态码来的,----> Upload组件的onChange事件
handleChange = ({ file, fileList }) => {
this.props.dispatch({
type: 'upload/setLocale',
payload: {
fileList
},
}) // 这个改变文件fileList一定要加上,不然没用
if (file.status === 'error') { // 失败情况下status为error
message.error(file.response.msg);
} else if (file.status === 'done') {
message.success('图片上传成功');
}
};
最后,前端从fileList中去挑选出符合要求的状态(status)进行处理