bo-oz
bo-oz

Reputation: 2872

How to POST file data to a presigned S3 URI with Vue?

I'm building a Vue app with a Rails backend. I'm following some articles online that suggested a workflow in which I:

  1. Let my Rails API generate a pre-signed S3 url
  2. Which I get through an API request in my Vue app
  3. I use the data from that request to POST the actual image directly to S3

The first two steps are working fine, but I'm struggling to understand how to include the filedata in the request that is of type: 'multipart/form-data'.

My Code is as follows, I use vuetify as a component library:

<template>
    <v-form>
      <v-file-input v-model="file" label="File input"></v-file-input>
      <v-btn class="mt-2" block bottom color="primary" @click="submit">Opslaan</v-btn>
    </v-form>
</template>

<script>
import { axios_request } from '../_helpers';

export default {
  name:'AccountImageForm',
  data: () => ({
    file: {}
  }),
  methods: {
    filesChange(event) {
      this.file = event.target.files[0]
    },
    submit() {
      axios_request('/api/v1/images/upload_url')
        .then(
          response => {
            // this response contains the pre-signed info
            var data = {
              ...response.url_fields,
              file: this.file
            }
            var headers = { 'Content-Type': 'multipart/form-data' }
            axios_request(response.url, 'POST', data, headers)
              .then(
              response => {
                console.log(response)
              }
            )
          }
        )
    },
  }
}
</script>

This requests fails with the following error

<Error>
  <Code>MalformedPOSTRequest</Code>
  <Message>The body of your POST request is not well-formed multipart/form-data.</Message>
  <RequestId>x</RequestId>
  <HostId>xx</HostId>
</Error>

When I look at my original formData, it seems that the filedata is empty. Also the size of the request is not big enough, so my assumption is that the file is missing. Is there some additional work to serialize the file for this request?

Thanks

Upvotes: 2

Views: 4108

Answers (1)

Phil
Phil

Reputation: 164901

The issue is that you're trying to post multipart/form-data but are sending a JS object literal which Axios is probably just stringifying.

What you need to do instead is create a FormData instance.

For example

response => {
  // convert your url_fields into a FormData instance
  const data = Object.entries(response.url_fields).reduce((fd, [ key, val ]) =>
      (fd.append(key, val), fd), new FormData())

  // now add the file
  data.append('file', this.file)

  axios_request(response.url, 'POST', data)
    .then(...)
}

Upvotes: 5

Related Questions