How to send File uploads using fetch via Netlify forms?

I can’t find any documentation on file uploads for Netlify forms using fetch for submission.

I am using Formik with yup for validation and need to send a form with a range of fields, including one field with multiple file uploads.

The form is successfully posting to Netlify with all other data using Object.keys, encodeURIComponent and .join("&"). But this won’t work for Files as it needs to send the File object. Any idea how I can send this data along with other fields so that Netlify recognises and has access to the files from the Form alongside this data?

This article was useful to get me so far with encoding data to post via fetch, but not for files. https://www.netlify.com/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/

Really appreciate the help.

Hi @danwebb, while there is no documentation that specifically covers file uploads for react apps, we do have an example in a repo from the author of that article. Let me know if that helps.

1 Like

Thank you for the reply Dennis. Looks like that could be useful yes.
I’ll let you know how I get on.

1 Like

Using that repo as a guide, I was able to get a ‘single’ File sent after validation. However I can’t seem to get Netlify to recognise multiple files within the Filelist object that is sent for a single field. I even tried creating a new array with the given File objects instead, but the response for that field Netlify side is then blank:

function encode(data) {
const formData = new FormData();

for (const key of Object.keys(data)) {
    if(key === 'repairs_files'){
        const files = Object.entries(data[key]);
        let filesArr = [];
        files.map(file => {
           return filesArr.push(file[1]);
        })
        formData.append(key, filesArr);
    } else {
        formData.append(key, data[key]);
    }
}
return formData;

}

So I guess the question is in what format does Netlify accept ‘multiple’ File uploads for a single field. Or does it only accept single File uploads? And therefore I’d need to create 3 fields for single File uploads?

Yup, I think we have an unstated limit of one file upload per field. Does that work better for you if you reconfigure it to be one-per?

Sure, thanks for letting me know. Yeah, I’ll need to modify the UI and will let the client know it’s not currently achievable in one field.

It might be useful to add that to the Forms docs, as I spent quite a while establishing whether it would work or not. Keep up the good work :slight_smile:

Great suggestion, Dan. We got an enhancement request filed with our documentarians :slight_smile:

Hi @fool and @danwebb just wondering if there has ben any progress made on Netlify forms supporting multiple file uploads?

Hi @macksol. Not to my knowledge. I’m sure @fool can help?

I wouldn’t expect any changes to this behavior anytime in the near future. As promised, we will follow up here if things ever do change, which is definitely possible but not at all guaranteed.

For anyone else wondering how to handle multiple file uploads with Netlify Forms, if you’re using React you can use a combination of react-dropzone and JSZip to zip the files client side. Here’s the basic idea that I just got working on a site…

First, react-dropzone has an onDrop callback…

<Dropzone onDrop={this.handleDrop}>
  {({ getRootProps, getInputProps }) => (
    <section>
      <div {...getRootProps()}>
        <input {...getInputProps()} name="files" />
        <p>Drag n' drop some files here, or click to select files</p>
      </div>
    </section>
  )}
</Dropzone>

Where the magic happens…

  handleDrop = (files) => {
    const zip = new JSZip();

    files.forEach((file) => zip.file(file.name, file));

    zip.generateAsync({ type: 'blob' }).then((blob) => {
      const zippedFiles = new File([blob], 'whatever.zip', {
        lastModified: Date.now(),
        type: 'application/zip'
      });

      this.setState({
        zippedFiles
      });
    });
  };
  handleSubmit = (e) => {
    e.preventDefault();

    const form = e.target;

    axios({
      url: '/',
      method: 'POST',
      data: this.encodeData({
        'form-name': form.getAttribute('name'),
        files: this.state.zippedFiles
      })
    })
  };
  encodeData = (data) => {
    const formData = new FormData();

    Object.keys(data).forEach((key) => formData.append(key, data[key]));

    return formData;
  };
2 Likes

My example uses Axios, but you get the idea! :slight_smile:

Awesome! Thanks so much for sharing that :trophy:

Hello @chocobuckle thanks for the example code, have you got this working in full anywhere which is sharable? like Codepen or an open source repo?

It was used on a gated site that’s only accessible upon receiving an email invite - and I’m not at liberty to send those invites to just anyone I’m afraid. But the code above was pretty similar to what I used in production - the only change I ended up making was changing the file type in the handleDrop function from blob to uint8array…

  zip.generateAsync({ type: 'uint8array' }).then((data) => {
    const zippedFiles = new File([data], 'whatever.zip', {
      lastModified: Date.now(),
      type: 'application/zip'
    });

    return zippedFiles;
  });

I also ended up putting all the JSZip functionality into a web worker, just to take a little stress off the main browser thread.