在Flutter中保存照片(尤其是相机胶卷)

时间:2018-04-12 13:16:11

标签: file camera dart save flutter

我无法弄清楚如何保存到相机胶卷(相当于React Native' s CameraRoll saveToCameraRoll()

  1. Flutter camera建议使用path_provider来获取应用程序目录,但它似乎没有选项来获取相机胶卷目录路径。

  2. 我在CameraController.capture

  3. 上收到例外

    相关更改(以及差异形式的相关更改)位于:https://gist.github.com/briankung/45f9d8438baab59ddcd3b6f3fe811d99

    我的整个main.dart位于下方以便轻松复制(搜索QUESTION:以查找相关部分):

    import 'dart:async';
    import 'dart:io';
    
    import 'package:flutter/material.dart';
    import 'package:camera/camera.dart';
    import 'package:flutter/services.dart';
    import 'package:path_provider/path_provider.dart';
    
    List<CameraDescription> cameras;
    
    Future<Null> main() async {
      SystemChrome.setPreferredOrientations([
        DeviceOrientation.portraitUp,
        DeviceOrientation.portraitDown
      ]);
    
      cameras = await availableCameras();
      runApp(new CameraApp());
    }
    
    class CameraApp extends StatefulWidget {
      @override
      _CameraAppState createState() => new _CameraAppState();
    }
    
    class _CameraAppState extends State<CameraApp> {
      String _appDirectoryPath;
      CameraController controller;
    
      Future<void> _requestAppDirectory() async {
        // QUESTION: `path_provider` doesn't have getCameraRollDirectory()
        Directory _appDirectory = await getApplicationDocumentsDirectory();
    
        setState(() {
          _appDirectoryPath = _appDirectory.path;
        });
      }
    
      @override
      void initState() {
        super.initState();
        _requestAppDirectory();
        controller = new CameraController(cameras[0], ResolutionPreset.medium);
        controller.initialize().then((_) {
          if (!mounted) {
            return;
          }
          setState(() {});
        });
      }
    
      @override
      void dispose() {
        controller?.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        if (!controller.value.initialized) {
          return new Container();
        }
    
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.red,
          ),
          home: new Scaffold(
            body: new Center(
              child: new AspectRatio(
                aspectRatio: controller.value.aspectRatio,
                child: new CameraPreview(controller),
              ),
            ),
            floatingActionButton: new FloatingActionButton(
              tooltip: 'Increment',
              child: new Icon(Icons.camera),
              onPressed: () {
                print('capturing');
                print(_appDirectoryPath);
                // QUESTION: this errors out
                controller.capture(_appDirectoryPath);
              },
            ),
            floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
          ),
        );
      }
    }
    

    日志信息如下:

    I/flutter ( 5471): capturing
    I/flutter ( 5471): /data/user/0/com.example.mycameraapp/app_flutter
    W/LegacyRequestMapper( 5471): convertRequestMetadata - control.awbRegions setting is not supported, ignoring value
    W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0.
    W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0.
    I/RequestThread-0( 5471): Received jpeg.
    I/RequestThread-0( 5471): Producing jpeg buffer...
    W/LegacyRequestMapper( 5471): convertRequestMetadata - control.awbRegions setting is not supported, ignoring value
    W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0.
    W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0.
    E/flutter ( 5471): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
    E/flutter ( 5471): CameraException(IOError, Failed saving image)
    E/flutter ( 5471): #0      CameraController.capture (package:camera/camera.dart:234:7)
    E/flutter ( 5471): <asynchronous suspension>
    E/flutter ( 5471): #1      _CameraAppState.build.<anonymous closure> (file:///Users/briankung/workspace/mobile/flutter/my_camera_app/lib/main.dart:84:24)
    E/flutter ( 5471): #2      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:478:14)
    E/flutter ( 5471): #3      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:530:30)
    E/flutter ( 5471): #4      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
    E/flutter ( 5471): #5      TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:161:9)
    E/flutter ( 5471): #6      TapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:123:7)
    E/flutter ( 5471): #7      GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27)
    E/flutter ( 5471): #8      _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:147:20)
    E/flutter ( 5471): #9      _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22)
    E/flutter ( 5471): #10     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7)
    E/flutter ( 5471): #11     _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7)
    E/flutter ( 5471): #12     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7)
    E/flutter ( 5471): #13     _invoke1 (dart:ui/hooks.dart:134:13)
    E/flutter ( 5471): #14     _dispatchPointerDataPacket (dart:ui/hooks.dart:91:5)
    I/RequestQueue( 5471): Repeating capture request cancelled.
    

    谢谢!

    忘记版本号:

    $ flutter --version
    Flutter 0.2.8 • channel beta • https://github.com/flutter/flutter.git
    Framework • revision b397406561 (10 days ago) • 2018-04-02 13:53:20 -0700
    Engine • revision c903c217a1
    Tools • Dart 2.0.0-dev.43.0.flutter-52afcba357
    
    // pubspec.yaml
    
    camera:
      dependency: "direct main"
      description:
        name: camera
        url: "https://pub.dartlang.org"
      source: hosted
      version: "0.1.2"
    path_provider:
      dependency: "direct main"
      description:
        name: path_provider
        url: "https://pub.dartlang.org"
      source: hosted
      version: "0.4.0"
    

3 个答案:

答案 0 :(得分:4)

您可以使用pub.dev/packages/gallery_saver中的 gallery_saver ,将视频和图像同时保存在Android和iOS的图库/照片中。

您只需要为其提供文件或URL的临时路径,即可保存本地文件和来自网络的文件。

这是它的用法:

GallerySaver.saveVideo(String path);
GallerySaver.saveImage(String path);

在成功保存案例文件后,两个函数都返回true,而以其他任何方式返回false。

我的团队开发了此插件。

答案 1 :(得分:3)

不幸的是,我不认为扑动目前会暴露这种功能。

您最好的选择可能是编写插件或使用Platform Channels来执行此操作。您可以像this example中一样使用临时目录,然后将路径传递给android,在那里您将读取文件并将其插入到图库中:

MediaStore.Images.Media.insertImage(
      getContentResolver(), 
      yourBitmap, 
      yourTitle , 
      yourDescription
);`

如果您在其中一个flutter存储库中创建功能请求,并且有人决定通过为您编写插件来帮助您,那么您可能会很幸运。

答案 2 :(得分:3)

您收到的错误是因为您试图将照片保存到/data/user/0/com.example.mycameraapp/app_flutter,它是目录,而不是文件。

您可以使用flutter_photokit包将照片/视频保存到iOS上用户的相机胶卷/自定义相册。您需要将照片捕获到设备的临时目录或应用程序目录,然后从那里将文件传输到用户的相机胶卷。示例的相关部分如下所示:

// At the top
import 'package:flutter_photokit/flutter_photokit.dart';

// Function in _CameraAppState
void _captureAndSaveToCameraRoll() async {
  String outputFilePath = '$_appDirectoryPath/test.jpg';
  // Capture the photo to the app directory
  await controller.capture(outputFilePath);
  // Save the photo to the user's camera roll
  FlutterPhotokit.saveToCameraRoll(filePath: outputFilePath);
}

...

// In your build function
floatingActionButton: new FloatingActionButton(
  tooltip: 'Increment',
  child: new Icon(Icons.camera),
  onPressed: _captureAndSaveToCameraRoll,
)

免责声明:我是该插件的作者。