如何使用带有 <script setup> 的 Axios / Vue 将文件上传到 Django REST API 后端

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

我在下面设置了 Vue 前端/Django REST API 后端存储库。 我正在努力寻找一种方法将文件发送到 Django 后端。

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

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

我能够确认我可以使用 Django API 界面上传文件(下图) Django API interface

并且还使用了 postman 桌面应用程序。

我这里的具体问题是,当用户选择文件时,我不知道如何将文件数据附加到 POST 请求。

在 Vue 存储库中,vue 文件中有一个 App.vue 和表单。

<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>


我有这个 POST 请求 createStoreDoc(),这会起作用,但还不会将文件附加到请求。

我想用或合成 API 而不是选项 API 来实现这个。 请随意分叉回购协议来回答这个问题。

感谢您阅读并分享您的知识。

这里我会怎么做:

你在 Content-Type header 中将 application/json 替换为 multipart/form-data :

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",
  },
});

并创建一个 FormData 实例并将您的数据附加到 createStoreDoc 函数中:

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);
    });
}

此时正在提出的请求:

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

这里我们在表单数据中看到了文件,但它是空的,因为输入有错误所以我们修复它:

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

然后,由于那个错误,它在 formState 中仍然是 null :

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

所以我们通过添加 appendFile 函数来修复它:

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

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

作为旁注,我们在状态中添加了文件名,并在 createStoreDoc 函数中使用了它:

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

我们收到了请求:

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

文件作为二进制数据上传