You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel10+Inertia.js+Vue3用户头像上传更新失败求助

Laravel 10 + Inertia.js + Vue 3 头像上传后端收不到文件问题

使用Laravel10、Inertia.js和Vue.js3开发用户头像上传功能时,提交Edit.vue/EditAvatar.vue中的表单后,Laravel的UserController的update方法无法获取到头像文件(通过dd(request())查看发现请求属性为空),导致无法验证和更新新头像。前端通过console.log可以正常查看选中的头像数据,但后端接收不到,问题出在EditAvatar.vue组件的提交逻辑上。

相关代码

Edit.vue

<Layout>
    <div id="main-div">
        <h1 class="text-center">Change Your Data</h1>
        <EditName :user="user"/>
        <EditPhoneNumber :user="user"/>
        <EditEmail :user="user"/>
        <EditAvatar :user="user"/>
    </div>
</Layout>
</template>

<script>
import Layout from "../../Layout/App.vue";
import { Link } from "@inertiajs/vue3";
import EditName from "./Edit/EditName.vue";
import EditEmail from "./Edit/EditEmail.vue";
import EditPhoneNumber from "./Edit/EditPhoneNumber.vue";
import EditAvatar from "./Edit/EditAvatar.vue";

export default {
    components: {
        Layout,
        EditName,
        EditEmail,
        EditPhoneNumber,
        EditAvatar,
        Link,
    },
    props: {
        errors: Object,
        user: {
            type: Object,
            required: true,
        },
    },
};
</script>

<style scoped>
#main-div {
    margin-top: 140px;
    margin-bottom: 140px;
}
</style>

EditAvatar.vue

<div class="row justify-content-center text-center mx-auto">
    <div class="col-12 col-sm-8 col-md-6">
        <form
            enctype="multipart/form-data"
            class="form mt-5"
            @submit.prevent="submitAvatar"
        >
            <div class="form-group mt-3">
                <label for="avatar" class="text-dark">Profile Image:</label
                ><br />
                <input
                    type="file"
                    name="avatar"
                    id="avatar"
                    class="form-control border-primary"
                    @input="form.avatar = $event.target.files[0]"
                />
            </div>
            <div class="d-flex justify-content-end align-items-center my-3">
                <div class="form-group ms-3">
                    <button
                        type="submit"
                        class="btn btn-warning text-primary py-2 px-3"
                    >
                        Save Profile Image
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
</template>

<script setup>
import { defineProps } from 'vue';
import { useForm } from "@inertiajs/vue3";
import { router } from '@inertiajs/vue3'

const props = defineProps({
    user: Object,
});

const form = useForm({
    avatar: null,
});

function submitAvatar() {
    // console.log("Avatar data:", form.avatar);
    router.put(`/profile/${props.user.id}/edit`, {
        _method: 'put',
        avatar: form.avatar,
    });
}
</script>

<style></style>

UserController edit方法

public function edit($id)
{
    $user = User::find($id);
    $user['avatar'] = asset('storage/' . $user->avatar);

    return Inertia::render("User/Edit")->with('user', $user);
}

UserController update方法

public function update(){
    $id = auth()->user()->id;

    dd(request());

    $user = User::find($id);
    $validated = [];

    if (request()->has('name')) {
        $validated = request()->validate([
            'name' => 'required|min:3|max:40',
        ]);
    }

    if (request()->has('phone_number')) {
        $validated = request()->validate([
            'phone_number' => 'required|min:8|max:20',
        ]);
    } 

    if (request()->has('email')) {
        $validated = request()->validate([
            'email' => 'required|email|unique:users,email',
        ]);
    }

    if (request()->has('avatar')) {
        $validated = request()->validate([
            'avatar' => 'required|image',
        ]);

        $avatarPath = request()->file('avatar')->store('profile', 'public');
        $validated['avatar'] = $avatarPath;

        if($user->avatar) {
            Storage::disk('public')->delete($user->avatar);
        }
    };

    if (request()->has('password') && Hash::check(request()->old_password, $user->password)) {
        request()->validate([
            'old_password' => 'required',
            'password' => 'required|min:3|confirmed',
        ]);

        $user->update([
            'password' => Hash::make(request()->password),
        ]);

        return redirect()->route('home')->with('success', 'Password updated successfully');
    }

    $user->update($validated);

    return redirect()->route('home')->with('message', 'Profile updated successfully!');
}

问题原因

直接使用router.put传递File对象时,Inertia默认会将请求数据序列化为JSON格式,但JSON无法传输File对象,导致后端接收不到文件数据。

解决方案

修改EditAvatar.vue的提交逻辑,利用Inertia的useForm提供的submit方法来处理文件上传,该方法会自动创建FormData并设置正确的请求头(multipart/form-data):

<script setup>
import { defineProps } from 'vue';
import { useForm } from "@inertiajs/vue3";

const props = defineProps({
    user: Object,
});

const form = useForm({
    avatar: null,
});

function submitAvatar() {
    form.put(`/profile/${props.user.id}/edit`, {
        preserveScroll: true,
        onSuccess: () => {
            form.reset('avatar'); // 上传成功后重置文件输入框
        },
    });
}
</script>

额外优化建议

  1. 后端update方法可以注入Request实例,代码更规范:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;

public function update(Request $request){
    $user = auth()->user();
    $validated = [];

    if ($request->hasFile('avatar')) {
        $validated = $request->validate([
            'avatar' => 'required|image|max:2048', // 限制文件大小为2MB
        ]);

        if($user->avatar) {
            Storage::disk('public')->delete($user->avatar);
        }
        $validated['avatar'] = $request->file('avatar')->store('profile', 'public');
    }

    // 其他字段的验证逻辑...

    $user->update($validated);

    return redirect()->route('home')->with('message', 'Profile updated successfully!');
}
  1. 前端可以添加文件类型验证,只允许图片文件:
<input
    type="file"
    name="avatar"
    id="avatar"
    class="form-control border-primary"
    accept="image/*" <!-- 只允许选择图片文件 -->
    @input="form.avatar = $event.target.files[0]"
/>

内容的提问来源于stack exchange,提问作者4leb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 01:15:55