react antd + node egg實現圖片上傳並保存在本地文件夾,file和stream

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章