Flutter应用如何上传图片至Laravel服务器 替代图片链接提交方式
全流程改造实现
整体改造逻辑分四个部分,不需要额外引入复杂组件,直接替换原有实现即可:
- 移除原来手动输入图片链接的输入框,替换为本地图片选择交互
- 将原来的纯文本JSON POST请求改为Multipart格式请求,同步携带图片文件和分类文本
- 改造Laravel后端的文件接收逻辑,将上传的图片存储到本地磁盘
- 将存储后的图片路径写入分类表的icon字段
第一步:Flutter端基础依赖准备
- 引入
image_picker包,实现本地相册选图能力 - 引入
dio包,用于支持Multipart格式的文件上传
第二步:替换原有UI交互
完全移除原来绑定图片链接的TextFormField,替换为带图片预览的可点击选择区域,改造后的完整UI代码如下:
Column( children: [ // 原图片链接输入框替换为图片选择组件 GestureDetector( onTap: () async { // 触发相册选图 final pickedImage = await ImagePicker().pickImage(source: ImageSource.gallery); if(pickedImage != null) { setState(() { _selectedCategoryImage = File(pickedImage.path); }); } }, child: Container( height: 100, margin: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(8) ), // 未选图显示提示,选图后显示预览 child: _selectedCategoryImage == null ? const Center(child: Text('请选择分类图标')) : Image.file(_selectedCategoryImage!, fit: BoxFit.contain), ), ), TextFormField( controller: categoryName, decoration: const InputDecoration(labelText: 'Category Name'), ), !isLoading ? RaisedButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30.0), ), color: Colors.deepPurple, onPressed: () { // 提交前校验是否选择了图片 if(_selectedCategoryImage == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请先选择分类图标')) ); return; } _postCategory( context, categoryName.text, _selectedCategoryImage! ); setState(() { isLoading = true; }); }, child: const Text( "提交分类", style: TextStyle( fontSize: 20, color: Colors.white, ), ), ) : const CircularProgressIndicator() ], )
注:原来的
categoryIconTextEditingController可以直接删除,不再需要手动输入链接的能力。
第三步:改造Flutter端请求逻辑
原来的纯JSON请求无法携带二进制图片文件,需要替换为Multipart格式构造,改造后的_postCategory方法如下:
// 提前定义状态变量,存储选好的图片 File? _selectedCategoryImage; _postCategory(BuildContext context, String categoryName, File imageFile) async { // 构造Multipart表单数据 var formData = FormData.fromMap({ 'categoryName': categoryName, // 图片字段和后端接收的字段名保持一致 'categoryIcon': await MultipartFile.fromFile( imageFile.path, // 给上传的图片加唯一文件名,避免重名覆盖 filename: 'category_icon_${DateTime.now().millisecondsSinceEpoch}.jpg' ) }); // 替换为你自己的分类接口地址 var response = await Dio().post( 'https://你的域名/api/category/store', data: formData ); var resData = response.data; if (resData['result'] == true) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const CategoryList())); } }
第四步:改造Laravel后端接收逻辑
原来的逻辑直接取文本输入值存库,现在需要增加文件存储、参数校验的能力,改造后的store方法如下:
public function store(Request $request) { // 参数校验,避免空值、非法文件报错 $validated = $request->validate([ 'categoryName' => 'required|string|max:255', 'categoryIcon' => 'required|image|max:2048', // 限制最大2M的图片文件 ]); // 将图片存储到storage/app/public/category_icons目录 $iconPath = $request->categoryIcon->store('category_icons', 'public'); $category = new Category(); $category->name = $validated['categoryName']; // 拼接图片可访问路径存入数据库 $category->icon = 'storage/' . $iconPath; if($category->save()){ return response()->json(['result' => true, 'name' => $category->name]); } return response()->json(['result' => false, 'message' => '分类保存失败']); }
后端配套注意事项
- 首次使用文件存储时,需要在Laravel项目根目录执行
php artisan storage:link,创建公开访问的软链接,否则存储的图片无法通过外部域名访问 - 接口返回值统一改为JSON格式,避免前端解析redirect响应出现格式错误
- 如果接口启用了CSRF校验,需要确保请求头携带合法的token,或将对应路由排除在CSRF校验规则外,否则会返回419错误
内容的提问来源于stack exchange,提问作者Pramod Yadav
相关产品推荐
相关产品推荐

