Search code examples
reactjsredux-formreact-dropzone

How to get base64 out of react-dropzone plugin with redux-form library


I'm working on a project which has integrated react-dropzone integrated within redux-form library. At the moment, when I drop an image in the dropzone and send it via API, the payload passed is the following:

{
    "preview": "blob:localhost:3000/jasd8as0da09sdas98d0a9s8d9as09s8das90sd",
    "path": "this_is_my_test_image.jpg"
}

None of the above is useful for the backend. I need to pass binary or base64. This is my jsx where the dropzone is called as redux-form component:

<form ....>
      <div className="form__form-group">
          <div className="form__form-group-field">
            <Field
              name='image'
              component={renderDropZoneField}
            />
          </div>
        </div>
</form>

The renderDropZoneField includes the following:

const DropZoneField = ({
  value, customHeight, name, onChange,
}) => {
  const files = value;
 

  const onDrop = (file) => {
    onChange(file.map(fl => Object.assign(fl, {
      preview: URL.createObjectURL(fl),
    })));
  };

  const removeFile = (index, e) => {
    e.preventDefault();
    onChange(value.filter((val, i) => i !== index));
  };

  return (
    <div className={`dropzone dropzone--single${customHeight ? ' dropzone--custom-height' : ''}`}>
      <Dropzone
        accept="image/jpeg, image/png"
        name={name}
        multiple={false}
        onDrop={(fileToUpload) => {
          onDrop(fileToUpload);
        }}
      >
        {({ getRootProps, getInputProps }) => (
          <div {...getRootProps()} className="dropzone__input">
            {(!files || files.length === 0)
            && (
              <div className="dropzone__drop-here">
                <span className="lnr lnr-upload" /> Drop file or click here to upload
              </div>
            )}
            <input {...getInputProps()} />
          </div>
        )}
      </Dropzone>
      {files && Array.isArray(files) && files.length > 0
      && (
          <aside className="dropzone__img">
            {
              !show && <img src={files[0].preview} alt="drop-img" />
            }
            {
              show && <AlertMessage />
            }
            
            <p className="dropzone__img-name">{files[0].name}</p>
            <button className="dropzone__img-delete" type="button" onClick={e => removeFile(0, e)}>
              Remove
            </button>
          </aside>
        )
      }
    </div>
  );
};

const renderDropZoneField = ({ input, customHeight }) => (
  <DropZoneField
    {...input}
    customHeight={customHeight}
  />
);

export default renderDropZoneField;

I've found this comment on github:https://github.com/react-dropzone/react-dropzone/issues/146 where the guys from the react-dropzone plugin suggest to use the following code:

onDropHandler(files) {      
      var file = files[0]
      const reader = new FileReader();
      reader.onload = (event) => {
        console.log(event.target.result);
      };
      reader.readAsDataURL(file);
}

I have made this changes to the drop constant:

const onDrop = (file) => {
    onChange(file.map(fl => Object.assign(fl, {
      preview: URL.createObjectURL(fl),
      base64: convertToBase64(fl). <---- this is the change
    })));

where the convertToBase64 function converts an image into a base64. Here my function:

export const convertToBase64 = (file) => {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.readAsDataURL(file);
    fileReader.onload = () => {
      resolve(fileReader.result);
    };
    fileReader.onerror = (error) => {
      reject(error);
    };
  });
};

Before sending the image via axsio API, this is how it looks like in the console.log: enter image description here

As you can see the Promise has been resolved and there is a base64 code in the result, but when I send it via API, the payload looks like this:

{
    "image": [
        {
            "path": "unnamed.jpeg",
            "preview": "blob:http://localhost:3000/e270e022-44e2-4d6b-b34a-dff1fce65033",
            "base64": {}
        }
    ]
}

Base64 is sent as an empty object. Any help is much appreciated.

Joe


Solution

  • I found the solution. It's was quite easy but didn't think about it at that moment.

    You need to store the result somewhere (in my case localstorage) and then pick it up from the DropZone component. Here the changes:

    export const convertToBase64 = (file) => {
      return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        fileReader.readAsDataURL(file);
        fileReader.onload = () => {
          resolve(fileReader.result);
          localStorage.setItem('base64', fileReader.result);
        };
        fileReader.onerror = (error) => {
          reject(error);
        };
      });
    };
    
    

    Then you need to make these changes:

    const onDrop = (file) => {
        convertToBase64(file)
    
        onChange(file.map(fl => Object.assign(fl, {
          preview: URL.createObjectURL(fl),
          base64: localStorage.getItem('base64');
        })));