Flutter上传多张图片

25 篇文章 0 订阅

2020-10-22更新:刚刚发布了一篇博客,一次性选择多张图片上传的demo,有需要的童鞋可以参考一下,下面附上链接:

https://blog.csdn.net/Gemini_Kanon/article/details/109215163

最近研究了几天上传图片,看了视频资料后上传一张图片是没问题了,自己有深究了一下一次上传多张图片的情况,自己琢磨出个思路~

首先引入依赖: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<SignCommitPage> {
  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: <Widget>[
          Row(
            children: <Widget>[
              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: <Widget>[
          _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<Part> 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博客里有~如果哪里代码有错误搞不出来可以联系小编帮忙!

这些代码是小编自己琢磨加找资料编写的,如果有错误请指出,哪位兄台有更好的方法小编想随时虚心请教~

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
为了在Flutter中实现图片上传,你可以使用`image_picker`和`dio`插件来调用系统相册和相机,并将选中的图片上传到服务器。 首先,你需要在Flutter项目的`pubspec.yaml`文件中添加`image_picker`和`dio`插件的依赖。 接下来,你可以使用`image_picker`插件的`getImage`方法来调用系统相册或相机,例如: ``` import 'package:image_picker/image_picker.dart'; final picker = ImagePicker(); // 调用相册 final pickedFile = await picker.getImage(source: ImageSource.gallery); // 调用相机 final pickedFile = await picker.getImage(source: ImageSource.camera); ``` 请注意,如果你需要支持多选图片,你需要在`pub.dev`上查找其他插件。 一旦你选择了图片,你可以使用`dio`插件将图片上传到服务器,例如: ``` import 'package:dio/dio.dart'; final dio = Dio(); FormData formData = FormData.fromMap({ 'file': await MultipartFile.fromFile(pickedFile.path), }); Response response = await dio.post('YOUR_UPLOAD_URL', data: formData); ``` 这里的`YOUR_UPLOAD_URL`是你的图片上传接口的URL,你需要根据实际情况进行替换。 以上是在Flutter上传图片的基本方法,你可以根据实际需求进行更多的定制和处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [flutter基础:调用图库及图片上传](https://blog.csdn.net/weixin_40652755/article/details/109724270)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值