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)进行处理

你可能感兴趣的:(随笔&笔记,React,egg,node)