【问题标题】:Flutter save a network image to local directoryFlutter 将网络图片保存到本地目录
【发布时间】:2018-09-02 10:48:31
【问题描述】:

在 Flutter 中如何将网络中的图片保存到本地目录。

我是编码和解码图像的新手。谁能指出我正确的方向?

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    如果您只想将图像(例如:.png)保存到设备,您可以通过简单的 get (http/http.dart) 和 File (dart:io) 轻松实现。

    为此,您可以参考以下示例:

    var response = await http.get(imgUrl);
    Directory documentDirectory = await getApplicationDocumentsDirectory();
    File file = new File(join(documentDirectory.path, 'imagetest.png'));
    file.writeAsBytesSync(response.bodyBytes); // This is a sync operation on a real 
                                               // app you'd probably prefer to use writeAsByte and handle its Future
    

    请注意,在上面的案例中,我使用了 dart pub 中的“path_provider”包。总体而言,您至少会导入以下项目:

    import 'dart:io';
    import 'package:http/http.dart' as http;
    import 'package:path/path.dart';
    import 'package:path_provider/path_provider.dart';
    

    【讨论】:

    【解决方案2】:

    对此有一个简单的答案和一个更复杂的答案。我会描述复杂的,然后给你简单的。

    复杂的答案是您可以使用NetworkImage 手动缓存图像,解析它并获取图像流。获得图像流后,您可以使用 Flutter 的文件读写功能将其保存到文件系统(this 是了解更多信息的好资源) - 您还需要使用一个名为 PathProvider 的插件,它可以获得正确的路径适用于该链接中描述的 iOS 和 Android。您还想跟踪您下载的所有图像,并可能在一定时间后删除它们。在使用它们创建 Image 小部件之前,您还必须读回这些文件。

    这给了你很多控制权,但需要一些工作(虽然不是一个疯狂的数量,但如果你是 Flutter 的新手,可能不是你现在想做的事情,这取决于你为什么要保存图像) .

    简单的答案是包来救援!其他人已经遇到过这个问题,并为你写了一个插件来解决它,所以你不必考虑它! p>

    有关插件的信息,请参阅cached_network_image package

    你需要在pubspec.yaml中添加你的依赖

    dependencies:
      cached_network_image: "^0.3.0"
    

    导入:

    import 'package:cached_network_image/cached_network_image.dart';
    

    并使用它!

    new CachedNetworkImage(
       imageUrl: "http://imageurl.png",
       placeholder: new CircularProgressIndicator(),
       errorWidget: new Icon(Icons.error),
    ),
    

    请注意,这会下载并显示图像 - 如果您想单独执行这些操作,可以使用 new CachedNetworkImageProvider(url) 并使用 new Image 显示它。

    【讨论】:

    • 我尝试使用这个包 cached_network_image 但这依赖于另一个名为 flutter_cache_manager 的包,该包在执行flutter packages get 命令时失败。跨度>
    • 很奇怪。不知道为什么会这样 - 我刚刚测试过了,没有问题。也许尝试不同版本的 cached_network_image,或将其设置为 cached_network_image: any(尽管这并不是真正推荐的)。您也可以尝试添加对 flutter_cache_manager 的依赖,然后看看会发生什么。
    • @mtmckenzie 嘿,我让它工作了。由于 Dart 2.0 兼容性,插件版本无法正常工作。现在它的更新和工作正常。感谢您的指导。
    • 你能说如何将这个缓存的图像保存到单独的路径
    • 答案中已经描述了如何使用 NetworkImage 来做到这一点。查看文档,如果您无法弄清楚,最好打开一个新问题。另一种选择是查看 CacheManger 以及它保存缓存的位置 - 我认为它使用 PathProvider 的 getTemporaryDirectory,因此图像文件可能保存在某个地方。
    【解决方案3】:

    在浏览了代码和颤动文档之后。我找到了适用于 iOS 和 Android 的方法和类,就到这里了。

    助手类

    import 'dart:async';
    import 'dart:io' as Io;
    import 'package:image/image.dart';
    
    import 'package:flutter_cache_manager/flutter_cache_manager.dart';
    import 'package:path_provider/path_provider.dart';
    class SaveFile {
    
      Future<String> get _localPath async {
        final directory = await getApplicationDocumentsDirectory();
    
        return directory.path;
      }
       Future<Io.File> getImageFromNetwork(String url) async {
    
         var cacheManager = await CacheManager.getInstance();
         Io.File file = await cacheManager.getFile(url);
         return file;
       }
    
       Future<Io.File> saveImage(String url) async {
    
        final file = await getImageFromNetwork(url);
        //retrieve local path for device
        var path = await _localPath;
        Image image = decodeImage(file.readAsBytesSync());
    
        Image thumbnail = copyResize(image, 120);
    
        // Save the thumbnail as a PNG.
        return new Io.File('$path/${DateTime.now().toUtc().toIso8601String()}.png')
          ..writeAsBytesSync(encodePng(thumbnail));
      }
    }
    

    类的用法

    class HomePageState extends State<HomePage>{
    
    Future<Null> _launched ;
    
      Widget _showResult(BuildContext context, AsyncSnapshot<Null> snapshot){
        if(!snapshot.hasError){
          return Text('Image is saved');
        }
        else{
          return const Text('Unable to save image');
        }
      }
    
      Future<Null> _saveNetworkImage(String url) async{
        try{
           await SaveFile().saveImage(url);
        }
        on Error catch(e){
          throw 'Error has occured while saving';
        }
      }
       @override
       Widget Build(BuildContext context){
           return new Scaffold(
                key: _scaffoldKey,
                appBar: new AppBar(
                    title: new Text('Image'),
                ),
                body: Column(
                      children: <Widget>[
                           IconButton(icon: Icon(Icons.save), onPressed: (){
                         setState(() {
                           _launched =_saveNetworkImage(url);
                         });
                        }),
                          new FutureBuilder<Null>(future: _launched ,builder: _showResult),
                      ],
                ),
              );
       }
    }
    

    【讨论】:

      【解决方案4】:

      要将网络图像保存在本地系统中,您需要使用 ImagePickerSave dart 插件。在 pub.yaml 文件中添加 dart 插件:image_picker_saver: ^0.1.0 并调用以下代码保存图像。 URL是网络图片的图片URL

      void _onImageSaveButtonPressed(String url) async {
        print("_onImageSaveButtonPressed");
        var response = await http
            .get(url);
      
        debugPrint(response.statusCode.toString());
      
        var filePath = await ImagePickerSaver.saveFile(
            fileData: response.bodyBytes);
        var savedFile= File.fromUri(Uri.file(filePath));
      }
      

      【讨论】:

      • 你能告诉我图像的存储位置吗?我想将我的图像保存在特定目录中。
      • 保存在你的应用包名下或者你可以找到安装文件管理器在图片下查看
      • 如果您同时使用 image_picker,此插件将不起作用。当您尝试访问设备的库时,它会使应用程序崩溃。
      【解决方案5】:

      关注此网址flutter save network image

      代码片段

      import 'package:flutter/material.dart';
      import 'dart:async';
      import 'dart:io';
      import 'package:path_provider/path_provider.dart';
      import 'package:http/http.dart' as http;
      
      class NetworkToLocalImage extends StatefulWidget{
       String url;
      
       NetworkToLocalImage(this.url);
      
       @override
       _LoadImages createState() => new _LoadImages(url);
       }
      
      class _LoadImages extends State<NetworkToLocalImage>{
      
       String url;
       String filename;
       var dataBytes;
      
       _LoadImages(this.url){
       filename = Uri.parse(url).pathSegments.last;
       downloadImage().then((bytes){
        setState(() {
          dataBytes = bytes;
        });
      });
      }
      
      Future<dynamic> downloadImage() async {
      String dir = (await getApplicationDocumentsDirectory()).path;
      File file = new File('$dir/$filename');
      
      if (file.existsSync()) {
        print('file already exist');
        var image = await file.readAsBytes();
        return image;
      } else {
        print('file not found downloading from server');
        var request = await http.get(url,);
        var bytes = await request.bodyBytes;//close();
        await file.writeAsBytes(bytes);
        print(file.path);
        return bytes;
      }
      }
      
       @override
       Widget build(BuildContext context) {
       // TODO: implement build
       if(dataBytes!=null)
       return new Image.memory(dataBytes);
       else return new CircularProgressIndicator();
      }
      }
      

      【讨论】:

      • 你至少应该总结一下链接后面的页面,以防它在将来消失。此外,它是一个 404...
      【解决方案6】:

      我在做这件事时遇到了很多麻烦,所以我想用一个简单的例子来解释一下上面的答案,这个例子在启动时下载一个文件,将它保存到本地目录。我用注释//%%% 标记了几行,以显示可以在应用程序第二次运行时注释掉的行。 (因为它不需要下载才能显示......它已经在设备本身上:

      import 'package:flutter/material.dart';
      import 'package:http/http.dart' show get;
      import 'dart:io';
      import 'package:path_provider/path_provider.dart';
      
      void main() => runApp(MyApp());
      
      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            title: 'Test Image',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: MyHomePage(title: 'Test Image'),
          );
        }
      }
      
      class MyHomePage extends StatefulWidget {
        MyHomePage({Key key, this.title}) : super(key: key);
        final String title;
        @override
        _MyHomePageState createState() => _MyHomePageState();
      }
      
      class _MyHomePageState extends State<MyHomePage> {
        @override
        initState() {
          _downloadAndSavePhoto();
          super.initState();
        }
      
        _downloadAndSavePhoto() async {
          // Get file from internet
          var url = "https://www.tottus.cl/static/img/productos/20104355_2.jpg"; //%%%
          var response = await get(url); //%%%
          // documentDirectory is the unique device path to the area you'll be saving in
          var documentDirectory = await getApplicationDocumentsDirectory();
          var firstPath = documentDirectory.path + "/images"; //%%%
          //You'll have to manually create subdirectories
          await Directory(firstPath).create(recursive: true); //%%%
          // Name the file, create the file, and save in byte form.
          var filePathAndName = documentDirectory.path + '/images/pic.jpg';
          File file2 = new File(filePathAndName); //%%%
          file2.writeAsBytesSync(response.bodyBytes); //%%%
          setState(() {
            // When the data is available, display it
            imageData = filePathAndName;
            dataLoaded = true;
          });
        }
      
        String imageData;
        bool dataLoaded = false;
      
        @override
        Widget build(BuildContext context) {
          if (dataLoaded) {
            return Scaffold(
              appBar: AppBar(
                title: Text(widget.title),
              ),
              body: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    // imageData holds the path AND the name of the picture.
                    Image.file(File(imageData), width: 600.0, height: 290.0)
                  ],
                ),
              ),
            );
          } else {
            return CircularProgressIndicator(
              backgroundColor: Colors.cyan,
              strokeWidth: 5,
            );
          }
        }
      }
      

      这是我的 pubspec.yaml 文件:

      http: ^0.12.0+2
        path_provider: 1.5.0
      

      【讨论】:

        【解决方案7】:

        您可以使用此功能从相机上传和获取 Url(download) 图像 firebase-存储

         Future _upIm()async {
              final StorageReference firebaseStorageRef =
              FirebaseStorage.instance.ref().child(DateTime.now().toString());
              final StorageUploadTask task =
              firebaseStorageRef.putFile(_storedImage);
        
              var downUrl=await (await task.onComplete).ref.getDownloadURL();
              var url =downUrl.toString();
              print(url);
        
              setState(() {
                uploadImage=url;
              });
        
            }
        

        【讨论】:

          【解决方案8】:

          受 Paras 启发,CacheManager.getInstance() 已弃用

          final DefaultCacheManager defaultCacheManager = DefaultCacheManager();
          final fileInfo = await defaultCacheManager.getFileFromCache(imageUrl);
          ImageGallerySaver.saveImage(image);
          

          下面是saveImage函数

          // save raw data to gallery with permission
          static Future<void> saveImage(Uint8List image) async {
            try {
              if (Platform.isIOS) {
                await PermissionHandler().requestPermissions([PermissionGroup.photos]);
                PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.photos);
                if (permission == PermissionStatus.granted) {
                } else {
                  throw 'denied';
                }
              } else if (Platform.isAndroid) {
                await PermissionHandler().requestPermissions([PermissionGroup.storage]);
                PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.storage);
                if (permission == PermissionStatus.granted) {
                } else {
                  throw 'denied';
                }
              }
              await ImageGallerySaver.saveImage(image);
            } catch (e) {
              throw e;
            }
          }
          

          【讨论】:

            【解决方案9】:

            您可以使用image_downloader

            • 对于 ios,图片保存在照片库中。
            • 对于Android,图像保存在Environment.DIRECTORY_DOWNLOADS 或指定位置。通过调用inExternalFilesDir(),无需指定权限。
            • 通过callback()获取进度状态。

            下面是最简单的例子。它将被保存。

            await ImageDownloader.downloadImage(url);
            

            【讨论】:

              【解决方案10】:

              【讨论】:

              • 最好总结一下内容,而不是仅仅链接到视频作为答案。此外,从视频的快速扫描来看,它似乎只是对计算机的文件系统执行此操作,而实际上并未在具有额外限制的 android/ios 上运行。
              • @AjayKumar 该视频在处理 Android 或 iOS 文件系统时没有帮助。移动文件系统还必须处理视频中根本没有处理的其他限制中的权限。
              猜你喜欢
              • 1970-01-01
              • 2021-03-29
              • 1970-01-01
              • 2021-07-04
              • 1970-01-01
              • 1970-01-01
              • 2020-08-15
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多