axios发送PUT请求时同时上传图片与文本的正确方法
问题根源
你遇到的字段不更新、上传失败问题,来自三个实现错误:
- 发送
multipart/form-data类型请求时,不能直接传递普通JavaScript对象作为请求体,必须使用FormData实例封装所有文本、文件字段 - 手动设置
Content-Type: multipart/form-data会丢失请求必须的boundary边界分隔符,服务端无法正确拆分解析请求内容,这个头应该交给浏览器/axios自动生成,禁止手动配置 - 表单配置和事件绑定有冗余错误:
enctype="form.data"是无效值,同时表单submit事件绑定了不存在的方法、提交按钮又重复绑定点击事件,可能导致重复触发请求。
正确实现方案
1. 修改请求逻辑
所有需要和文件一起提交的字段,全部装入FormData实例,请求时不要手动设置Content-Type头,修改后的脚本代码如下:
<script setup> import { ref } from "vue"; import { useRouter } from "vue-router"; import http from "../http-common"; const router = useRouter(); // 绑定文件选择input的ref const fileInput = ref(null); // 注意:请确认profile变量已在当前作用域正确定义 const form = ref({ first_name: profile.userProfile.first_name, last_name: profile.userProfile.last_name, photo: null }); function handleImageSelect() { // 读取选中的文件 form.value.photo = fileInput.value.files[0]; } const updateProfile = async () => { try { // 构造FormData实例 const formData = new FormData(); // 追加文本字段 formData.append('first_name', form.value.first_name); formData.append('last_name', form.value.last_name); // 有选中图片时追加文件字段 if (form.value.photo) { formData.append('photo', form.value.photo); } const res = await http.put("user/profile", formData, { headers: { Accept: "application/json" // 不要手动配置Content-Type,axios会自动识别FormData并设置正确的请求头 } }); if (res.data.success) { alert(res.data.message); router.push("/profile"); } } catch (error) { console.log(error); } }; </script>
2. 修正表单配置
修正enctype错误值,去掉重复的事件绑定,核心表单代码如下:
<form class="space-y-8 divide-y divide-hrgray" enctype="multipart/form-data" @submit.prevent="updateProfile" > <div class="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-center sm:border-t sm:border-hrgray sm:pt-5" > <label for="photo" class="block text-sm font-medium text-gray-700" > Photo </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <div class="flex items-center"> <input ref="fileInput" @change="handleImageSelect" type="file" /> </div> </div> </div> <div class="pt-8 space-y-6 sm:pt-10 sm:space-y-5"> <div class="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-hrgray sm:pt-5" > <label for="first-name" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2" > First name </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <input type="text" v-model="form.first_name" name="first-name" id="first-name" autocomplete="given-name" class="max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" /> </div> </div> <div class="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-hrgray sm:pt-5" > <label for="last-name" class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2" > Last name </label> <div class="mt-1 sm:mt-0 sm:col-span-2"> <input type="text" v-model="form.last_name" name="last-name" id="last-name" autocomplete="family-name" class="max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" /> </div> </div> </div> <div class="pt-5 pb-10"> <div class="flex justify-end"> <button type="submit" class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-book hover:bg-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" > Save </button> </div> </div> </form>
兼容处理
如果部分后端框架无法正确解析PUT方法提交的multipart/form-data请求,可以采用方法伪装的方式:将请求改为POST方法,在FormData中追加formData.append('_method', 'PUT'),大部分后端框架(比如Laravel、Rails)都能识别这个参数并按照PUT路由逻辑处理请求。
另外需要检查你封装的http-commonaxios实例,如果全局配置了固定的Content-Type: application/json默认头,要么移除这个固定配置让axios自动适配请求类型,要么在本次请求的headers配置中显式将Content-Type设为undefined,覆盖默认值。
内容的提问来源于stack exchange,提问作者Mena
相关产品推荐
相关产品推荐

