Laravel+Vue多图上传提交时提示media字段必填验证报错
Laravel+Vue多图上传提交报media必填错误
问题现象
开发多图上传功能时,选中图片后提交表单,接口返回如下验证错误:
{ "message": "The given data was invalid.", "errors": { "media": ["The media field is required."] } }
从报错信息判断后端未接收到media字段传值,但Vue Devtools中可看到media数组已经存储了已上传的临时图片数据。
相关实现代码
ProductController代码
public function store(Request $request) { $this->validate($request, [ 'title' => 'required|max:255', 'price' => 'required|integer', 'media'=>'required', ]); $product = Product::create([ 'title' => $request->title, 'price' => $request->price, ]); if ($request->image) { $imageName = time() . '_' . uniqid() . '.' . $request->image->getClientOriginalExtension(); $request->image->move(public_path('storage/product'), $imageName); $product->image = '/storage/product/' . $imageName; $product->save(); } foreach($request->media as $image){ $from = public_path('tmp/uploads/'.$image); $to = public_path('product_images/'.$image); File::move($from, $to); $product->images()->create([ 'name' => $image, ]); } return response()->json($product, 200); }
ImageController代码
public function store(Request $request){ $path = public_path('tmp/uploads'); if (!file_exists($path)) { mkdir($path, 0777, true); } $file = $request->file('image'); $name = uniqid() . '_' . trim($file->getClientOriginalName()); $file->move($path, $name); return ['name'=>$name]; } public function getImages(Product $product){ $images = $product->images; return ['media'=>$images]; }
api.php路由配置
Route::resource('product','ProductController'); Route::post('/upload', [ImageController::class, 'store'])->name('upload'); Route::get('/media/{product}', [ImageController::class, 'getImages'])->name('product.images');
Vue上传组件代码
import axios from 'axios' export default { data(){ return{ media:[], loading:false, } }, methods:{ async fileChange(event){ this.loading=true let files = event.target.files for(var i=0; i < files.length; i++){ let formData = new FormData let url = URL.createObjectURL(files[i]) formData.set('image', files[i]) const {data} = await axios.post(this.server, formData) this.media.push({url:url, name:data.name, size:files[i].size, type:files[i].type}); } this.loading=false this.media_emit() }, remove(index){ this.media.splice(index,1) this.media_emit() }, media_emit(){ this.$emit('media',this.media) } }, mounted() { this.$emit('media',this.media) }, }
问题原因
- 上传组件仅通过自定义事件将media数据抛给父组件,父组件提交商品创建表单时,没有将media字段加入请求参数,后端自然无法接收到对应值
- 组件内存储的media是包含预览url、文件名、文件大小、文件类型的对象数组,后端逻辑实际只需要临时图片的文件名,直接传递整个对象会导致后续文件移动逻辑报错
- 如果使用
FormData格式提交表单,数组类型参数需要以media[]作为键名追加,否则PHP无法将参数解析为数组格式
修复方案
1. 补全父组件表单提交逻辑
父组件监听子组件抛出的media事件,存储选中的图片数据,提交表单时提取文件名加入请求参数:
// 父组件逻辑示例 data() { return { form: { title: '', price: '', mediaList: [] } } }, methods: { // 接收子组件传递的media数据 getMediaList(list) { this.form.mediaList = list }, async submit() { const formData = new FormData() formData.append('title', this.form.title) formData.append('price', this.form.price) // 提取文件名,按数组格式追加到FormData this.form.mediaList.forEach(item => { formData.append('media[]', item.name) }) // 发起提交请求 await axios.post('/api/product', formData) } }
父组件中引入上传组件时绑定事件:
<UploadComponent @media="getMediaList" server="/api/upload" />
2. 完善后端参数校验
修改ProductController中的校验规则,明确media参数为数组,同时校验数组内元素为合法文件名字符串:
$this->validate($request, [ 'title' => 'required|max:255', 'price' => 'required|integer', 'media' => 'required|array', 'media.*' => 'string' ]);
3. 增加文件存在判断
移动临时文件前先判断文件是否存在,避免临时文件丢失导致的报错:
foreach($request->media as $image){ $from = public_path('tmp/uploads/'.$image); $to = public_path('product_images/'.$image); if (file_exists($from)) { File::move($from, $to); $product->images()->create([ 'name' => $image, ]); } }
内容的提问来源于stack exchange,提问作者rahi bd
相关产品推荐
相关产品推荐

