最近研究了几天上传图片,看了视频资料后上传一张图片是没问题了,自己有深究了一下一次上传多张图片的情况,自己琢磨出个思路~
首先引入依赖:image_picker,当然还有别的大家可以自行选择,有个multy_image_picker依赖也可以,是一次选择多张图片的~然后在你需要上传的文件中引入文件:import 'package:image_picker/image_picker.dart';和import 'dart:io';接下来是代码:
3月11日:更新上传照片和删除图片代码,下面是全部代码
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:Config.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
class SignCommitPage extends StatefulWidget {
final arguments;
SignCommitPage({Key key, this.arguments}) : super(key : key);
_SignCommitPageState createState() => _SignCommitPageState(this.arguments);
}
class _SignCommitPageState extends State {
final arguments;
_SignCommitPageState(this.arguments);
//控制listview滚动条
ScrollController _controller = new ScrollController();
//用来存放图片的容器
List _img = new List();
//File类型,用来上传图片
File _image;
//用来存放上传的图片返回的id,后面删除图片用
List _imgId = new List();
//输入框组件
Widget _textFieldWidget () {
//listview滚动条保持在最后
Timer(Duration(milliseconds: 0), () => _controller.jumpTo(_controller.position.maxScrollExtent));
return Container(
padding: EdgeInsets.only(left: ScreenAdapter.width(20.0), right: ScreenAdapter.width(20.0)),
child: Column(
children: [
Row(
children: [
this._img == null ? Expanded(
flex: 1,
child: Text(""),
) : Expanded(
flex: 1,
child: Container(
width: double.infinity,
height: ScreenAdapter.width(150.0),
child: ListView.builder(
controller: _controller, //滚动条控制
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: this._img.length, //循环_img渲染出listview
itemBuilder: (context, index){
return InkWell(
child: Container(
width: ScreenAdapter.width(150.0),
height: ScreenAdapter.width(150.0),
margin: EdgeInsets.only(right: ScreenAdapter.width(10.0)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.0),
border: Border.all(
style: BorderStyle.solid,
color: Colors.black26,
)
),
//隐藏id值,删除图片时可以直接获取到对应图片id
child: Visibility(
visible: false,
replacement: Image.network(this._img[index], fit: BoxFit.cover,),
child: Text(this._imgId[index]),
)
),
//长按删除图片
onLongPress: (){
Alert(
context: context,
type: AlertType.info,
title: "确定删除这张照片吗?",
buttons: [
DialogButton(
child: Text(
"确定",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () async {
//删除图片方法,将要删除的图片名称和id传递过来
_deleteImg(this._img[index], this._imgId[index]);
Navigator.of(context).pop();
},
),
DialogButton(
child: Text(
"取消",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.of(context).pop(),
)
],
).show();
},
);
},
),
),
),
InkWell(
child: Container(
width: ScreenAdapter.width(150.0),
height: ScreenAdapter.width(150.0),
margin: EdgeInsets.only(right: ScreenAdapter.width(10.0)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.0),
border: Border.all(
style: BorderStyle.solid,
color: Colors.black26,
)
),
child: Center(
child: Icon(Icons.camera_alt),
),
),
onTap: _openGallery,
)
],
)
]
)
);
}
//选择相册照片&上传
void _openGallery () async {
var imageUrl = await ImagePicker.pickImage(source: ImageSource.gallery, maxHeight: ScreenAdapter.width(150.0), maxWidth:ScreenAdapter.width(150.0));
setState(() {
//将获取到的图片路径赋给_image,上传
this._image = imageUrl;
});
//上传开始
String path = imageUrl.path;
//获取文件名
String imgName = path.substring(path.lastIndexOf("/") + 1, path.length);
//获取格式
String suffix = imgName.substring(imgName.lastIndexOf(".") + 1, imgName.length);
FormData formData = new FormData.from({
"userId" : "123456",
"file" : new UploadFileInfo(this._image, imgName)
});
var res = await Dio().post("${Config.domain}uploadImg", data: formData);
if (res.data["success"]) {
Fluttertoast.showToast(
msg: "${res.data["message"]}",
gravity: ToastGravity.CENTER,
timeInSecForIos: 3
);
setState(() {
//将返回的图片路径和对应id添加到_img和_imgId中
this._img.add("${Config.domain}" + "${res.data["data"]}");
this._imgId.add("${res.data["id"]}");
});
} else {
Fluttertoast.showToast(
msg: "${res.data["message"]}",
gravity: ToastGravity.CENTER,
timeInSecForIos: 3
);
}
}
//删除图片
void _deleteImg (img, id) async {
//请求后端方法
var api = "${Config.domain}deleteImg?id=" + id;
var res = await Dio().get(api);
if (res.data["success"]) {
Fluttertoast.showToast(
msg: "${res.data["message"]}",
gravity: ToastGravity.CENTER,
timeInSecForIos: 3
);
//成功的话将该图片和对应id分别从_img和_imgId中移除
setState(() {
this._img.remove(img);
this._imgId.remove(id);
});
} else {
Fluttertoast.showToast(
msg: "${res.data["message"]}",
gravity: ToastGravity.CENTER,
timeInSecForIos: 3
);
}
}
//页面
@override
Widget build(BuildContext context) {
ScreenAdapter.init(context);
return Scaffold(
appBar: AppBar(
leading: InkWell(
child: Icon(Icons.keyboard_return),
onTap: (){
Navigator.pop(context);
},
),
title: Text("上传图片"),
),
body: ListView(
children: [
_textFieldWidget(),
],
),
);
}
}
//Config.dart
class Config {
//域名定义成自己的即可
static String domain = "http://www.haha.com/";
}
后端代码,小编的后端是java:
//直接用@RestController了,前后端分离直接返回json数据就好了
@RestController
//一定要有这个注解,下面讲作用
@MultipartConfig
public class AppController {
//引包小编就不贴了,现在编程软件一般都有提示
@Autowired
private HttpServletRequest request;
//上传图片
String filesPath = "";
@RequestMapping(value = "/uploadImg", method = RequestMethod.POST)
public AppResult uploadImg (HttpServletRequest request) {
try {
//此处要提到上面@MultipartConfig注解了,flutter向后端post数据时,一般的字符串数据可以直接通过request.getParameter("xxx")获取到,但是无法获取图片,获取为null
//此处和request.getParameter("xxx")一样,你传的文件的key
Part part = request.getPart("file");
//获取文件名
String fileNames = getFileName(part);
//这里小编把文件重命名了一下,以免重名
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmsssss");
String id = sdf.format(new Date());
String[] splitName = fileNames.split("\\.");
String finalName = id + "." + splitName[splitName.length - 1];
//上传文件
filesPath = writeTo(finalName, part);
//这里可以实现你自己的逻辑,可以与数据库交互等等
return AppResult.success(true, "上传成功", 返回图片路径, 返回图片id);
} catch (IOException ie) {
ie.printStackTrace();
return AppResult.error(false, "网络错误,请稍后重试");
} catch (ServletException se) {
se.printStackTrace();
return AppResult.error(false, "上传失败,请稍后重试");
}
}
//获取文件名
private String getFileName(Part part) {
String head = part.getHeader("Content-Disposition");
String fileName = head.substring(head.indexOf("filename=\"")+10, head.lastIndexOf("\""));
return fileName;
}
//写文件
private String writeTo(String finalName, Part part) throws IOException {
String path = request.getSession().getServletContext().getRealPath("/uploadAppVisit");
if (!new File(path).exists() || !new File(path).isDirectory()) {
new File(path).mkdirs();
}
InputStream in = part.getInputStream();
OutputStream out = new FileOutputStream(path + "/" + finalName);
byte[] b = new byte[1024];
int length = -1;
while((length = in.read(b)) != -1){
out.write(b, 0, length);
}
in.close();
out.close();
return finalName;
}
//删除图片
@RequestMapping(value = "/deleteImg", produces={"application/json;charset=UTF-8"})
public AppResult deleteImg (String id) {
try {
//这里是删除方法,换成自己的
hahaService.deleteById(id);
return AppResult.ok(true, "删除成功", "");
} catch (Exception e) {
e.printStackTrace();
return AppResult.error(false, "删除失败请重试");
}
}
}
//AppResult.java,这里定义一个实体类用来返回结果,避免每个方法都要自己写一遍,也方便修改
public class AppResult {
private boolean success;
private String message;
private Object data;
private String id;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public AppResult (boolean success, String message) {
this.success = success;
this.message = message;
}
public AppResult (boolean success, String message, Object data) {
this.success = success;
this.message = message;
this.data = data;
}
public AppResult (boolean success, String message, Object data, String id) {
this.success = success;
this.message = message;
this.data = data;
this.id = id;
}
public static AppResult ok (boolean success, String message, Object data) {
return new AppResult(success, message, data);
}
public static AppResult success (boolean success, String message, Object data, String id) {
return new AppResult(success, message, data, id);
}
public static AppResult error (boolean success, String message) {
return new AppResult(success, message);
}
}
//同时上传多张照片获取方法,有时间把上传照片详细代码更上来
Collection parts = request.getParts();
if (parts.size()>0) {
String str="";
for (Part part : parts) {
if (part.getName().startsWith("file")) {
String fileNames = getFileName(part);
String path = writeTo(fileNames, part);
str += xxx;
}
}
}
选择图片实现,如下图:
选择图片后:
到这里选择图片上传和删除图片全部实现,各位可以直接拿走使用,里面还有个地图定位的功能这里没写,在这篇https://blog.csdn.net/Gemini_Kanon/article/details/104628500博客里有~如果哪里代码有错误搞不出来可以联系小编帮忙!
这些代码是小编自己琢磨加找资料编写的,如果有错误请指出,哪位兄台有更好的方法小编想随时虚心请教~