'How to upload files with Axios / Vue with <script setup> to Django REST API backend

I have both Vue frontend / Django REST API backend repository setup below. I am struggling to find a way to send the file to the Django backend.

Django backend https://github.com/TraitOtaku/Vue-File-Upload

Vue frontend https://github.com/TraitOtaku/Django-Rest-API-for-Fileupload

I was able to confirm that I can upload the files with the Django API interface(image below) Django API interface

and also using the postman desktop app.

My specific problem here is that when the user selects the file, I don't know how to attach the file data to the POST request.

In the Vue repo, there is an App.vue and form inside the vue file.

<template>
  <div class="container mt-5">
    formState:{{ formState }}
    <form enctype="multipart/form-data">
      <div class="form-group my-2">
        <label for="exampleInputEmail1">File Name</label>
        <input class="form-control" v-model="formState.name">
      </div>

      <div class="form-group my-2">
        <label class="form-label" for="customFile">Upload Store Documents</label>
        <input type="file" class="form-control" id="storeFile"
          @change="appendFile(event.target.name, event.target.files)" />
      </div>

      <button type="submit" class="btn btn-primary" @click.prevent="createStoreDoc">Submit</button>
    </form>

    <h1>Store Documents</h1>
    <div v-if="storeDoc">
      <div v-for="doc in storeDoc" :key="doc.id">
        <div>
          File ID:{{ doc.id }}
        </div>
        <div>
          File Name:{{ doc.name }}
        </div>
        <div>
          File Content:{{ doc.file }}
        </div>
        <hr>
      </div>
    </div>
  </div>
</template>

<script setup>
import axios from "axios";
import { ref, toRaw, reactive } from "vue";

const formState = reactive({
  name: "",
  file: null,
});


const apiClient = axios.create({
  baseURL: "http://127.0.0.1:8000/api/store-doc/",
  withCredentials: false,
  headers: {
    // Accept: "application/json",
    "Content-Type": "application/json",
  },
});

const storeDoc = ref(null)
const getStoreDoc = () => {
  apiClient.get()
    .then((response) => {
      storeDoc.value = response.data
      // console.log(storeDoc)
    })
}
getStoreDoc()


const createStoreDoc = () => {
  apiClient.post('', toRaw(formState))
    .then((response) => {
      console.log("success!" + response.data);
      getStoreDoc()
    })
    .catch((error) => {
      console.log(error);
    });
}



</script>


I have this POST request createStoreDoc() and this will work but won't attach file to the request yet.

I want to achieve this with or composition API, not Options API. Please feel free to fork the repo to answer this question.

Thank you for reading and lending your knowledge.



Solution 1:[1]

Here how i would do it :

you replace application/json by multipart/form-data in the Content-Type header :

const apiClient = axios.create({
  baseURL: "http://127.0.0.1:8000/api/store-doc/",
  withCredentials: false,
  headers: {
    // Accept: "application/json",
    "Content-Type": "multipart/form-data",
  },
});

and make a FormData instance and append your data to it inside createStoreDoc function :

const createStoreDoc = () => {
  let formData = new FormData();
  formData.append('name', formState.name);
  formData.append('file', formState.file, 'filename.docx');
  apiClient.post('', formData)
    .then((response) => {
      console.log("success!" + response.data);
      getStoreDoc()
    })
    .catch((error) => {
      console.log(error);
    });
}

request that is being made at this point :

curl 'http://127.0.0.1:8000/api/store-doc/' \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDhFZwmF0hYfGZyHU' \
  --data-binary $'------WebKitFormBoundaryDhFZwmF0hYfGZyHU\r\nContent-Disposition: form-data; name="name"\r\n\r\ntest\r\n------WebKitFormBoundaryDhFZwmF0hYfGZyHU\r\nContent-Disposition: form-data; name="file"\r\n\r\nnull\r\n------WebKitFormBoundaryDhFZwmF0hYfGZyHU--\r\n' \
  --compressed

Here we see the file in the form data but it's null, because there is an error on the input so we fix it :

<input type="file" class="form-control" id="storeFile"
          @change="appendFile($event.target.name, $event.target.files)" />

then, it's still null in the formState because of that error :

runtime-core.esm-bundler.js?5c40:218 Uncaught TypeError: _ctx.appendFile is not a function

So we fix it by adding that appendFile function :

const formState = reactive({
  name: "",
  file: null,
  filename: null
});

const appendFile = (name, files) => {
    formState.filename = name;
    formState.file = files[0];
}

as a side note, we added the filename in the state and used it in the createStoreDoc function :

formData.append('file', formState.file, formState.filename);
  

and we get that request :

curl 'http://127.0.0.1:8000/api/store-doc/' \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0p7GnwTxmLgGiCHx' \
  --data-binary $'------WebKitFormBoundary0p7GnwTxmLgGiCHx\r\nContent-Disposition: form-data; name="name"\r\n\r\ntest\r\n------WebKitFormBoundary0p7GnwTxmLgGiCHx\r\nContent-Disposition: form-data; name="file"; filename="filename.docx"\r\nContent-Type: image/png\r\n\r\n\r\n------WebKitFormBoundary0p7GnwTxmLgGiCHx--\r\n' \
  --compressed

File is uploaded as binary data

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1