flutter基础:调用图库及图片上传

摘要

本文主要介绍flutter调用系统相册、相机以及图片上传的实现方法,主要用到了image_picker以及dio插件,先上效果图:

1.调用图库及相机

添加插件

image_picker: ^0.6.7+12

ios:在ios/Runner/Info.plist文件下添加 NSPhotoLibraryUsageDescription NSCameraUsageDescription 及NSMicrophoneUsageDescription 三个key用于权限申请及描述,我这里就随便写了:

	<key>NSPhotoLibraryUsageDescription</key>
	<string>App需要您的同意,才能访问相册</string>
	<key>NSCameraUsageDescription</key>
    <string>App需要您的同意,才能访问相机</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>App需要您的同意,才能访问麦克风</string>

android:api29以下需要在配置文件(android/app/src/mainAndroidManifest.xml)application节点下添加:

 android:requestLegacyExternalStorage="true"

image_picker提供了

  • getImage(source: ImageSource.gallery)
  • getImage(source:ImageSource.camera)

分别调用相册以及相机,下面是界面布局:

 body: new Column(
        //crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          new RaisedButton(onPressed: openGallery, child: new Text('图库')),
          new RaisedButton(onPressed: takePhotos, child: new Text('拍照')),
          if (imagePath == null)
            new Expanded(child: new Center(child: new Text('未选择图片')))
          else
            new Expanded(
                child: new Center(
              child: Image.file(imagePath),
            )),
          new RaisedButton(
            onPressed: upLoadImg,
            child: new Text('上传'),
          )
        ],
      ),
      ..省略..

这里添加了两个按钮分别用于调用图库和调用相机,屏幕剩余部分则用于显示图片,若没选择图片则显示一个Text(‘未选择图片’);

调用方法:

import 'package:image_picker/image_picker.dart';
...省略其他代码....
//调用相册
 void openGallery() async {
    
    
    PickedFile pickedFile = await _picker.getImage(source: ImageSource.gallery);
    final File file = File(pickedFile.path);
    setState(() {
    
    
      imagePath = file;
    });
  }
//调用相机
  void takePhotos() async {
    
    
    PickedFile pickedFile = await _picker.getImage(
      source: ImageSource.camera,
    );
    final File file = File(pickedFile.path);
    setState(() {
    
    
      imagePath = file;
    });
  }

分别写了两个方法调用相册和相机,再通过 File(pickedFile.path)从返回的PickedFile获取到图片路径并更新给展示图片的Image。

另外: image_picker相册只能选择一张图片,需要多选的话可以到pub.dev中查找其他插件。

2.图片的上传

dio的引入

dio: 3.0.10

图片上传:

import 'package:dio/dio.dart';
//。。。省略。。。
 upLoadImg() async {
    
    
    if (imagePath == null) {
    
    
      showtoast('未选择图片!');
      return;
    }
    String path = imagePath.path;
    var name = path.substring(path.lastIndexOf("/") + 1, path.length);
    FormData formdata = FormData.fromMap(
      {
    
    
        "参数名xxx": await MultipartFile.fromFile(path,
            filename: name,)
      },
    );
    
    BaseOptions option = new BaseOptions(
        contentType: 'multipart/form-data', responseType: ResponseType.plain);

    Dio dio = new Dio(option);
    //application/json
    try {
    
    
      var respone = await dio.post<String>(
          "后台接口https//xxx.xxx...",
          data: formdata);
      if (respone.statusCode == 200) {
    
    
        showtoast('图片上传成功');
      }
    } catch (e) {
    
    
      print("e:" + e.toString() + "   head=" + dio.options.headers.toString());
    }
  }

通过MultipartFile.fromFile方法根据文件路径及文件名获取到文件,添加到表单并上传到服务器,非常简单。
最后说下我遇到的问题:图片上传格式错误,通过上述代码可以将文件上传到服务器了,接口也返回200,但是后台的伙伴发现文件后缀名变成了 .octet-stream,改成.jpg后可以正常显示。但是为什么变成了.octet-stream??查了很多资料说body参数的问题,需要改成form-data而不是binary,于是把代码改成:

BaseOptions option = new BaseOptions(
        contentType: 'multipart/form-data', responseType: ResponseType.plain);

    Dio dio = new Dio(option);

然而并没什么用,事实上通过FormData的方式上传参数contentType默认的就是multipart/form-data,最后发现需要在参数构建的时候指定文件类型MediaType。。。

 FormData formdata = FormData.fromMap(
      {
    
    
        "参数名xxx": await MultipartFile.fromFile(path,
            filename: name, contentType: MediaType('image', 'jpeg'))
      },
    );
注意:MediaType在http_parser包下,需要手动引入:
import 'package:http_parser/http_parser.dart';

3.完整的代码

最后附上完整代码

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http_parser/http_parser.dart';

class ImageSelector extends StatefulWidget {
    
    
  String title;

  ImageSelector({
    
    Key key, this.title}) : super(key: key);

  @override
  ImageState createState() => new ImageState(title);
}

class ImageState extends State<ImageSelector> {
    
    
  final _picker = ImagePicker();
  String title;
  File imagePath;

  ImageState(this.title);

  @override
  Widget build(BuildContext context) {
    
    
    // TODO: implement build
    return new Scaffold(
      appBar: new AppBar(title: new Text('$title')),
      body: new Column(
        //crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          new RaisedButton(onPressed: openGallery, child: new Text('图库')),
          new RaisedButton(onPressed: takePhotos, child: new Text('拍照')),
          if (imagePath == null)
            new Expanded(child: new Center(child: new Text('未选择图片')))
          else
            new Expanded(
                child: new Center(
              child: Image.file(imagePath),
            )),
          new RaisedButton(
            onPressed: upLoadImg,
            child: new Text('上传'),
          )
        ],
      ),
    );
  }

  void openGallery() async {
    
    
    PickedFile pickedFile = await _picker.getImage(source: ImageSource.gallery);
    final File file = File(pickedFile.path);
    setState(() {
    
    
      imagePath = file;
    });
  }

  void takePhotos() async {
    
    
    PickedFile pickedFile = await _picker.getImage(
      source: ImageSource.camera,
    );
    final File file = File(pickedFile.path);
    setState(() {
    
    
      imagePath = file;
    });
  }

  upLoadImg() async {
    
    
    if (imagePath == null) {
    
    
      showtoast('未选择图片!');
      return;
    }
    String path = imagePath.path;
    var name = path.substring(path.lastIndexOf("/") + 1, path.length);
    FormData formdata = FormData.fromMap(
      {
    
    
        "参数名xxx": await MultipartFile.fromFile(path,
            filename: name, contentType: MediaType('image', 'jpeg'))
      },
    );

    BaseOptions option = new BaseOptions(
        contentType: 'multipart/form-data', responseType: ResponseType.plain);

    Dio dio = new Dio(option);
    //application/json
    try {
    
    
      var respone = await dio.post<String>(
          "后台接口https//xxx.xxx...",
          data: formdata);
      if (respone.statusCode == 200) {
    
    
        showtoast('图片上传成功');
      }
    } catch (e) {
    
    
      print("e:" + e.toString() + "   head=" + dio.options.headers.toString());
    }
  }

  void showtoast(String toast) {
    
    
    Fluttertoast.showToast(
        msg: toast, gravity: ToastGravity.BOTTOM, textColor: Colors.grey);
  }
}

总结

简单实现了相册、相机的调用和图片的上传,解决了图片上传格式错误的问题。

猜你喜欢

转载自blog.csdn.net/weixin_40652755/article/details/109724270