node.js + react practice -Form whole stack in accordance with the specified path and file upload

Book connected to the back, spoke of "a new bomb using the same box" have unresolved issues, such as complex fields, files, upload pictures, this one will solve the problem of file uploads. Here is a scene in the new pop-up box to upload a picture, and the upload component on a Form in, and other text fields to be submitted with the interface.

Here there are several issues to be aware of:

  1. When the image upload best picture at the front end of the specified type, according to the type of the uploaded to the specified directory. For example, here is a new user, users upload pictures, so here is a specified type "user", then put upload the file to upload / user directory on the server. So easy to post-maintenance, should such a project document as a unified move to another server, as long as the upload directory copied out just fine.
  2. Upload component is generic, back to the front end of a path information after the upload is complete, is due to the use and design of the Form, then take this path to match the data in the form to be submitted to the new interface.

1. The back-end interface to upload files

1.1 multer

Previously used to when writing new data, request data middleware bodyParser , when parsing client requests, json type used to accept data, this is very convenient, but when uploading files is generally multipart / form-data this types, bodyParser this type can not be resolved. Then another middleware incorporated herein multer . multer specializing multipart / form-data type of form data, professional.

multer used in two ways, if only the general web applications, directly specifying dest, that is, upload path on it. If more control when uploading, you can use the storage options. Here I start from the simple, direct path to specify the file to upload a file.

// the specified file upload path 
var Upload = multer ({dest: path.join (__ dirname, './../public/upload/tmp')}); 

Use this module to node.js in the path, the relative path will ./../public/upload/tmp converted into the local computer path, pay attention to where we built a upload / tmp directory in public directory express projects, as for what is this temporary file tmp folder, please continue to read.

Then upload the definition of interfaces:

    router.post('/singleFile', upload.single('file'), function (req, res, next) {
    }) 

Here we define a api / base / singleFile interfaces, accept uploaded files in a named file Form tag, you can upload it to the file after this definition under public / upload / tmp directory.

1.2 designated upload directory

This specifies the path to upload multer way is a good start is specified, behind uploaded to this directory, that directory can not be a variable, then how can you pass over the tip according to the parameters specified directory upload pictures to it? I was here first thought is "cut" file. Since using a node.js, api file operations on files and ultimately cut. There are instructions on the official document, the callback function in addition to the file, you can also have req.body, if the text field data, in this req.body, this and bodyParser are similar.

app.post ( '/ Profile', upload.single ( 'Avatar'), function (REQ, RES, Next) {
   // req.file `avatar` information file 
  // req.body data having a text field, if present, 
}) 

With req.file, after req.body two objects rest of the work to node.js, the code is as follows:

// 文件上传
router.post('/singleFile', upload.single('file'), function (req, res, next) {
  if(req.body.fileLocation) {
    const newName = req.file.path.replace(/\\tmp/, '\\' + req.body.fileLocation) + path.parse(req.file.originalname).ext
    fs.rename(req.file.path, newName, err => {
      if (err) {
        res.json(result.createResult(false, { message: err.message }))
      } else {
        let fileName = newName.split('\\').pop()
        res.json(result.createResult(true, {Path: req.body.fileLocation} {$ `/` $ {fileName}}))
       } 
    }) 
  } the else { 
    res.json (result.createResult ( to false , {Message: 'file path is not specified' })) 
  } 
})

Note that here also the use of a module fs rename method, this method can modify the file and rename the file path, the file is cut. Here the method of tmp directory to replace is replaced with a front end pass over fileLocalhost, and then move the file to the directory fileLocation. Use the following postman to debug track about the implementation process:

postman request:

Uploaded to the tmp directory:

 Moved to the specified user directory:

 postman returned:

 

At this point, the interface was written, the following is calling this interface at the front. 

2. The front end of the Form Call Interface 

2.1 Definitions Field Type

In the last node.js + react practice full stack - the opening , the use of a uniform data add components to add data. columns.js the field type, text boxes are not specified, which is obviously not realistic, here coupled with a property type: file represents the data component is added, this field corresponds to a file upload component. In addition, if, there are restrictions on the size of the file type, where you can also add accept, size field. code show as below:

const thumb = { title: '头像', dataIndex: 'thumb', key: 'thumb', render: src => <img className={style.tableImg} alt='' src={ `${config.baseUrl.resource.upload}${src}` }/>, type: 'file', accept: 'image/gif,image/jpeg', size: 2 } 

2.2 Upload上传组件

剩下的就要研究一下ant design中的Upload组件,看一下文档就明白了。关键代码如下:

{field.map((f, index) => {
  switch (f.type) {
    case 'file':
      return <FormItem
        name='file'
        headers={headers}
        key={f.key}
        label={f.title}>
        {getFieldDecorator(f.key)(<div>
          <Upload
            name="file"
            accept={f.accept}
            data={data}
            listType="picture-card"
            showUploadList={false}
            action="http://localhost:3332/api/base/singleFile"
            beforeUpload={this.beforeFileUpload.bind(this, f)}
            onChange={this.handleFileChange.bind(this, f)}>
            {imageURL ? <img src={imageURL} alt="avatar" style={{ width: '100%' }} /> : uploadButton}
          </Upload>
        </div>)}
      </FormItem>
    default:
      return <FormItem key={f.key} label={f.title}>
        {getFieldDecorator(f.key, { rules: [{ validator: this.customerValidator.bind(this, f) }] })(<Input placeholder={'请输入' + f.title}/>)}
      </FormItem>
  }
})} 

name:这个是字段名字,如果是要调用api/base/singleFile这个接口,就要设置为file,和上面的upload.single('file')是对应起来的
accept:接受的文件类型,从columns.js中thumb字段中获取,也可以在beforUpload回调中验证类型
data:这个就是除了文件之外额外的参数,可以指定为{fileLocation: 'user'}表示要上传到user子目录,这里要赞美一下ant design,已经考虑了额外参数
listType:显示样式,参考antd design文档,不解释
showUploadList:同上,不解释
action:上传文件接口,注意这里要使用本地api文件中定义的接口,不能使用服务端的接口路径,否则会代理失败的
beforUpload:上传文件之前的钩子,这里要赞美一下ant design,可以额外传一个参数f,带入字段信息,这样就可以获取字段的accept,size信息,进行验证
onChange:文件状态改变时的钩子,继续赞美一下ant design,同上,可以额外传递一个参数

这里有一个小疑问:antd design中解释onChange:“上传中、完成、失败都会调用这个函数”,我测试了一下,确实会调用三次,但是有两次都返回了response,status都是done,和我想象的不一样。这上传成功了,按说有上传中,完成个回调,那都是done是怎么回事,“完成”调用了两次?

onChang回调:

到这里,接口已调通,文件已经能够成功的从前端传到后端了。 

2.3 Form获取文件路径

最后一个问题,这里使用Form组件填充,收集数据,Form中上传组件是单独的跑起来的,最后得到的是一个url,不是文件本身,如何将这个url给到form中呢?这里使用的是form.setFieldsValue({name: value})这个方法,简答粗暴。代码如下:

  handleFileChange(field, info) {
    let file = info.file
    if (file.response && file.response.success && file.response.data && file.response.data.path) {
      let { upload } = this.state
      upload.imageURL = `${config.baseUrl.resource.upload}${file.response.data.path}`
      // 为Form对应的字段设置值
      this.props.form.setFieldsValue({ [`${field.key}`]: file.response.data.path })
      this.setState({ upload })
      upload.loading = false
    }
  } 

注意这里FormItem是动态加载出来的,并不知道是那个字段,所以onChange回调中额外传递了参数f,这样,setFildsValue中就知道这是要设置Form中哪一个数据。

最后看一下效果:

上传文件:

 

数据表: 

未解决的问题:

1.上传过程中如果因为其他问题导致失败,并且是在转移之前失败,服务器上upload/tmp目录会有很多的垃圾文件,这里可以在转移之后把tmp目录中的文件全部删掉
2.文件的校验是放在beforUpdate钩子里通过全局提示message.error弹出,这个是不是可以放在getFieldDecorator的rules里面,体验会更好

 

Guess you like

Origin www.cnblogs.com/tylerdonet/p/12021663.html