Trouble with handling files in Netlify Function

So, I tested this and I’ve 2 options to offer:

  1. Continue using multipart/form-data:

Yes, you could continue using it, but with a catch. If you only need to upload a file with the form (and no other form fields), this would do the trick. In the above code, simply change it to Buffer.from(event.body, 'base64'). I repeat, there should not be any other data except the file. Not multiple files either, just 1 file! If you need multiple files, you’d have to send each file to a different serverless function. You can use the same function, but you’d have to invoke it again for each file. For form fields, you could use FormData(), but make sure that you exclude the file from it.

  1. Switch to application/json:

Yes, submitting a form with application/json might seem weird, but it works. However, there’s some extra client-side work that’s required. You need to convert the file to base64 and add each form field separately to the JSON payload. You could do it programatically too, but yeah that’s the workflow. Here’s an example:

const form = document.forms.fileForm
form.onsubmit = event => {
  event.preventDefault()
  new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(form.elements.file.files[0])
    reader.onload = () => resolve(reader.result)
    reader.onerror = error => reject(error)
  }).then(file => {
    fetch('/.netlify/functions/getData/', {
      body: JSON.stringify({
        file: file,
        name: form.elements.name.value
      }),
      headers: {
        'content-type': 'application/json'
      },
      method: 'post',
    }).then(response => {
      if (response.ok) {
        return response.json()
      } else {
        throw response.statusText
      }
    }).then(data => {
      console.log(data)
    }).catch(error => {
      console.log(error)
    })
  })
}

Finally, in the serverless function, no need to use Busboy anymore:

import { initializeApp } from 'firebase/app'
import { getDownloadURL, getStorage, ref, uploadString } from 'firebase/storage'

exports.handler = async event => {

  const payload = JSON.parse(event.body)

  return uploadString(ref(getStorage(initializeApp({
    /* firebase stuff */
  })), payload.name), payload.file.split(',')[1], 'base64').then(file => {

    return getDownloadURL(file.ref).then(url => {

      return {
        body: JSON.stringify({
          url: url
        }),
        statusCode: 200
      }

    })

  })

}

Basically, the file’s base64 content exists as: payload.file.split(',')[1]. The file gets encoded as follows:

data:application/pdf;base64,actual-base64-content

And thus, we need to split it to get the actual base64 content. That’s required at least to upload the file to Firebase, it might not be required for other providers - but if you wish to convert the base64 to some other format like Buffer or something else, you’d again have to use split(',')[1].

Using the JSON way, you get both, the form fields and the file - MD5 perfect.

Test it here:

https://stoic-brown-d4563d.netlify.app/

Name is supposed to be the file name that you’d like it to be saved as.

You’d get the download URL of the file in console or if the console trims it out, you could always check it in Network tab.