Flutter 图片选择及上传与后端处理
Flutter + Nodejs(express)实现多图片上传
·
Flutter 图片选择及上传与后端处理
1、环境及依赖
Flutter 3.3.8 (空安全版本)
image_picker: ^0.8.6+3
dio: ^5.0.1
Nodejs搭建express框架服务器
"express": "^4.17.1",
"multer": "^1.4.5-lts.1",
2、前端图片选择与上传
首先要明确的是 图片将以文件的格式,被包含在http报文请求实体中的form-data数据项中发送
2-1 dio_http.dart 用于封装dio 此处只介绍封装form-data数据的方法
// dio_http.dart
class DioHttp {
Dio? _client;
late BuildContext context;
static DioHttp of(BuildContext context) {
return DioHttp._internal(context);
}
// 构建请求实例方法
DioHttp._internal(BuildContext context) {
if (_client == null || context != this.context) {
this.context = context;
var option = BaseOptions(
baseUrl: 'http://10.0.2.2',
connectTimeout: Duration(seconds: 1000 * 10),
receiveTimeout: Duration(seconds: 1000 * 3),
extra: {'context': context},
);
var client = Dio(option);
this._client = client;
}
}
// 封装form-data数据的方法
Future<Response<Map<String, dynamic>>> postFormData(String path, params) async {
var options = Options(
contentType: 'multipart/form-data',
);
return await _client!.post(
path,
data: params,
options: options,
);
}
}
2-2 使用 ImagePicker 完成 CommonImagePicker 组件
封装一个图片选择器
注意:图片的上传在父组件完成,因此要将子组件中的图片发送到父组件
// 位于父组件中
CommonImagePicker(
onChange: (List<File> files) {
setState(() {
images = files;
});
},
),
// CommonImagePicker 组件(选择多张照片)
class CommonImagePicker extends StatefulWidget {
// 回调函数,用于更新父级组件
final ValueChanged<List<File>> onChange;
const CommonImagePicker({super.key, required this.onChange});
State<CommonImagePicker> createState() => _CommonImagePickerState();
}
// 由于ImagePicker都用的 XFile 格式,所以需要将 XFile 转为常用的 File格式
class _CommonImagePickerState extends State<CommonImagePicker> {
// 用于保存组件内的照片
List<XFile> files = [];
// 用于更新父级组件中的照片
List<File> fatherfiles = [];
// 弹出图片选择器,也就是打开本地相册
_pickImage() async {
final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return;
setState(() {
// 更新组件内的照片
files = files..add(image);
// 更新要发送给父组件的照片
// 将 XFile 转化为 File: File(item.path)
fatherfiles = files.map((i) => File(i.path)).toList();
});
// 通知父级更新
if (widget.onChange != null) {
widget.onChange(fatherfiles);
}
}
Widget build(BuildContext context) {
// ‘+’按钮
Widget addButton = GestureDetector(
onTap: () {
_pickImage();
},
behavior: HitTestBehavior.translucent,
child: Container(
width: 30,
height: 60,
color: Colors.grey,
child: Center(
child: Text(
'+',
style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.w100,
),
),
),
),
);
//单个图片组件
Widget wrapper(XFile imgUrl) {
var img = File(imgUrl.path);
return Stack(
children: [
// 展示图片
Image.file(
img,
height: 30,
width: 60,
fit: BoxFit.fill,
),
],
);
}
List<Widget> list = files.map((item) => wrapper(item)).toList()..add(addButton);
return Container(
padding: EdgeInsets.all(10),
child: Wrap(
spacing: 10,
runSpacing: 10,
children: list,
),
);
}
}
选择单张照片的代码差不多,就是不用数组来保存图像文件就行了,这里就不展开说了
2-3 父组件向后端发送请求
封装请求方法,因为需要在发送请求后拿到结果并进行后续处理,所以用Future
Future<String> uploadImageSingle(File file, BuildContext context) async {
if (file == null) return Future.value('');
// 将图片封装成 formData 格式
var formData = FormData();
formData.files.add(MapEntry(
"files", //后台接收的名字
MultipartFile.fromFileSync(file.path, filename: 'b'),
));
var res = await DioHttp.of(context).postFormData('/uploadImageSingle', formData);
var resString = jsonDecode(res.toString());
// nameList 是上传的图片的名字,数组类型
var nameList = resString['data'];
return Future.value(nameList);
}
在需要时调用,发起请求(在添加照片后或发送整体请求之前)
// 上传图片并获取图片名
var imagesString = await uploadImages(images, context);
3、后端处理
这里笔者是用的express框架搭建的服务器,过程比较简单就不说了
3-1 uploadImages.js
multer 插件用于处理 http报文 请求实体中的form-data数据
const express = require('express')
const router = express.Router()
const multer = require("multer");
const imagesRename = require('../utils/imagesRename')
// dest:指定存储位置
const upload = multer({ dest: __dirname + "/../uploadedImages" });
// 上传图片接口
// upload.any() 任意文件格式
router.post('/uploadImages', upload.any(), (req, res) => {
// 拿到请求实体中的form-data数据
const data = req.files
// 可以打印出来看看,里面是数组对象的形式,包含很多文件信息
// console.log(data)
var imagesNameList = []
for(let i = 0 ; i < data.length ; i++){
// 我们只需要文件的文件名,用于保存到数据库
imagesNameList.push(data[i].filename)
}
// 将上传的二进制文件转换成图片格式
imagesRename()
res.send({ status: 0, msg: '获取用户信息成功', data: imagesNameList })
})
module.exports = router
3-2 imagesRename.js
imagesRename 是一个将上传的二进制文件转换成图片格式的方法
const fs = require('fs')
function imagesRename() {
fs.readdir('./uploadedImages', (error, list) => {
if (error) {
throw error;
} else {
for (let i of list) {
if (i.indexOf('.jpg') == -1) {
fs.rename("./uploadedImages/" + i, "./uploadedImages/" + i + '.jpg', error => {
if (error) {
new Error(error);
return;
}
})
}
}
}
})
}
module.exports = imagesRename
更多推荐
所有评论(0)