Koa+React实现form表单上传文件

{app.params.name}} {app.params.name}} {app.params.name}}

在nodejs里面通过html的表单上传图片,跟php比较起来还是麻烦一些,特别是在react中使用表单提交含有文件的数据。

最近也在做这个操作,找了很多资料,一直都是有个co-busboy这个,其实它也是基于busboy去封装的,不过这个更适合在koa里面使用。

以往的表单提交,我们用浏览器去debug的时候,会看到,提交了一个post提交的数据,这个一般只是值的提交,不包括有文件,但是在react中,

好像就更加复杂了一些,对于表单提交要单独自己写一个提交的事件去处理,累死submit这样的jquey函数。

而且提交成功后,我们用chrome去debug的时候,会发现提交的是一个Request Payload这样的,跟Form Data不一样,而且我们在koa中,

用body也是获取不到对应的值的,一切都是繁琐加着繁琐,自己考虑一下,像php这样的,是人家都帮你做好了,用起来很顺手,最终需要解决的就是,

如果把提交的data从header中去解析出来,其实busboy做的还是很好的。下面我大概说下我是如何去操作的。


web使用的jquery,通过FormData

在componentDidMount方法中初始化一下submit。

let handleAction = this.props.handleAction;
$("#BackgroundForm").submit(function(e){
  let formData = new FormData($(this)[0]);
  $.ajax({
    url:'/theapp/upload/background',
    data:formData,
    dataType:'json',
    type:'post',
    cache: false,
    contentType: false,
    processData: false,
    beforeSend:function(){
      $('#submitButton').attr('disabled',true);
    },
    success:function(data){
      if(data.success == true){
        handleAction(true);
        $('#errorContainer').html('').hide();
      }else if(data.success == false){
        let message = '<ul><li>'+data.message+'!</li></ul>';
        $('#errorContainer').html(message).show();
        $('#submitButton').attr('disabled',false);
      }
    },error:function(err){
      console.log(err);
    }
  });
  return false;
});


在server中的操作是这样的

const upload = function (ctx, key, streamName) {
  return new Promise(function (resolve, reject) {
    ctx.qiniuClient.upload(fs.createReadStream(streamName), key, function (err, result) {
      if (err) {
        return reject(err);
      }
      resolve(result);
    });
  });
};
theapp.post('/upload/background', co.wrap(function *(ctx, next) {
  let parts = parse(ctx.req);
  let part;
  //初始化数据
  let title = '';
  let theme = '';
  let theorder = '';
  let token = '';
  let big_url;
  let thumb_url;
  //获取form-data提交的数据
  while (part = yield parts) {
    if (Array.isArray(part)) {
      let name = part[0];
      let value = part[1];
      if (name == 'title') {
        title = value;
      }
      if (name == 'theme') {
        theme = value;
      }
      if (name == 'theorder') {
        theorder = value;
      }
      if (name == 'access_token') {
        token = value;
      }
    } else {
      let streamName = '/tmp/' + Math.random();
      let stream = fs.createWriteStream(streamName);
      part.pipe(stream);
      if (part.fieldname == 'big_url') {
        big_url = streamName;
      }
      if (part.fieldname == 'thumb_url') {
        thumb_url = streamName;
      }
    }
  }
  if (!big_url) {
    throw new Error('请上传背景大图');
  }
  if (!thumb_url) {
    throw new Error('请上传背景小图');
  }
  if (!title) {
    fs.unlinkSync(big_url);
    fs.unlinkSync(thumb_url);
    throw new Error('名称不能为空');
  }
  if (!theme) {
    fs.unlinkSync(big_url);
    fs.unlinkSync(thumb_url);
    throw new Error('主题不能为空');
  }
  if (!theorder) {
    theorder = 0;
  }
  //名称判断存在
  const url = ctx.config.hostDomain + '/admin/query';
  const sql = `SELECT * FROM qeeniao.app_dft_background WHERE title = '${title}'`;
  const options = {
    timeout: ctx.config.httpTimeout,
    method: 'POST',
    data: {'sql': sql, 'access_token': token}
  };
  const requestData = yield urllib.request(url, options);
  let data = requestData.data.toString();
  data = JSON.parse(data);
  if (data.length > 0) {
    fs.unlinkSync(big_url);
    fs.unlinkSync(thumb_url);
    throw new Error('名称已经存在');
  }
  //上传图片
  let big_res = yield upload(ctx, {
    'key': 'image/background/hd/' + title + '.jpg'
  }, big_url);
  big_url = big_res['url'];
  let thumb_res = yield upload(ctx, {
    'key': 'image/background/thumbnail/' + title + '.jpg'
  }, thumb_url);
  thumb_url = thumb_res['url'];
  //提交数据库
  let postData = {
    'title': title,
    'theme': theme,
    'theorder': theorder,
    'big_url': big_url,
    'thumb_url': thumb_url,
    'access_token': token
  };
  let postUrl = ctx.config.hostDomain + '/admin/add/background';
  let postoptions = {
    timeout: ctx.config.httpTimeout,
    method: 'POST',
    data: postData
  };
  let postRes = yield urllib.request(postUrl, postoptions);
  postRes = postRes.data.toString();
  postRes = JSON.parse(postRes);
  ctx.body = postRes;
}));


这里使用了一个qn的库

在启动的时候我把他加入了context中

app.context.qiniuClient = qn.create({
  accessKey: "Your accessKey",
  secretKey: "Your secretKey",
  bucket: "Your bucket",
  origin: 'Your origin'
});


这样处理起来就没问题了,其实要是如果koa把busboy自己封装起来是最好的。



版权声明

durban 创作并维护的 Gowhich 博客采用 创作共用保留署名-非商业-禁止演绎4.0国际许可证。

本文首发于 Gowhich 博客( https://www.gowhich.com ),版权所有,侵权必究。

本文永久链接: https://www.gowhich.com/blog/727

comments powered by Disqus