先上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)進行處理