Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uploading an image with redux-form

I have a react.js redux-form that works and posts data back to my API but I need to also allow the submitter to upload an image with the form, ideally with a preview. I struggled a bit and have arrived at dropzone.js but I can't seem to get my form to actually POST the image data back.

render () {
  const FILE_FIELD_NAME = 'files';

    const renderDropzoneInput = (field) => {
      const files = field.input.value;
      return (
        <div>
          <Dropzone
            name={field.name}
            onDrop={( filesToUpload, e ) => field.input.onChange(filesToUpload)}
          >
            <div>Try dropping some files here, or click to select files to upload.</div>
          </Dropzone>
          {field.meta.touched &&
            field.meta.error &&
            <span className="error">{field.meta.error}</span>}
          {files && Array.isArray(files) && (
            <ul>
              { files.map((file, i) => <li key={i}>{file.name}<img src={file.preview}/></li>) }
            </ul>
          )}
        </div>
      );
    }

    return (
        <form onSubmit={this.props.handleSubmit(this.onSubmit)}>
          <div className="form-group">
            <Field name="files" component={renderDropzoneInput} />
          </div>
          <button type="submit" className="btn btn-default">Submit</button>
        </form>
    );
}

The files variable does get POSTed back to the API which is great but it contains the following:

[preview=blob:http://localhost:3000/bed3762e-a4de-4d19-8039-97cebaaca5c1]

Can anyone suggest how I get the actual binary data in to that variable please?

The full code is available here https://github.com/rushughes/dsloracle/blob/master/client/src/components/LandCreate/index.js

like image 307
RusHughes Avatar asked Jan 06 '18 05:01

RusHughes


People also ask

What is submitting in redux form?

There are two ways to give redux-form a function to run when your form is submitted: Pass it as an onSubmit prop to your decorated component. In which case, you would use onSubmit={this. props. handleSubmit} inside your decorated component to cause it to fire when the submit button is clicked.

Is redux form good?

Redux Form is very easy to use and implement, we just have to wrap our form component with the HOC provided by Redux Form and we are good to go. Applying validation to the form is very easy in Redux Form, we can apply validation to all the fields as well as validations for individual fields.


2 Answers

I recently had a similar issue and solved it by using the FileReader API to convert the blob url to Base64 (can also convert to binary string).

Then you send the Base64 or binary string to the server.

My example code:

onDrop(acceptedFiles: any): any {

    let images: any = this.state.Images;

    acceptedFiles.forEach((file: any) => {

        const reader: FileReader = new FileReader();
        reader.onload = () => {
            const fileAsBase64: any = reader.result.substr(reader.result.indexOf(",") + 1);
            images.push(fileAsBase64);
        };

        reader.onabort = () => console.log("file reading was aborted");
        reader.onerror = () => console.log("file reading has failed");

        reader.readAsDataURL(file);
    });

    this.setState(prevState => ({   
         Images: images,
    }));
}

If you want to send a binary string instead of base64 change reader.readAsDataURL(file); to reader.readAsBinaryString(file);

and this line: const fileAsBase64: any = reader.result.substr(reader.result.indexOf(",") + 1); can be simplified to const file: any = reader.result;

like image 166
Liam Kenneth Avatar answered Oct 19 '22 23:10

Liam Kenneth


Here are the steps for file-upload feature: (How to handle image data in your API)

  • Append your redux-form values to the FormData instance.

    let formData = new FormData();
    formData.append('myFile', files[0]);
    
  • Send multipart/form-data request from Client to your API with axios or fetch library:

  • Receive that multipart/form-data request in your API, process it with multer and then write the file to the disk storage or memory storage as follows:

    $ npm install --save multer
    
    const multer  = require('multer')
    
    const storage = multer.diskStorage({
      destination: function (req, file, cb) {
      cb(null, '/tmp/my-uploads')
     },
      filename: function (req, file, cb) {
      cb(null, file.fieldname + '-' + Date.now())
     }
    })
    
    const upload = multer({ storage: storage })
    
    const app = express()
    
    app.post('/upload', upload.single('myFile'), (req, res, next) => {
      // req.file is the `myFile` file
      // req.body will hold the text fields, if there were any
     })
    
  • (Optional) Serve files directly from your API with Express Serve-Static

like image 44
gokcand Avatar answered Oct 19 '22 21:10

gokcand