Flutter save a network image to local directory

2020-02-23 04:30发布

问题:

In Flutter how to save an image from network to the local directory.

I am new to encoding and decoding images. Can anyone point me in the right direction?

回答1:

There's a simple answer and a more complicated answer to this. I'll describe the complicated one and then give you the simple one.

The complicated answer is that you could manually cache images by using a NetworkImage, resolving it, and getting the image stream. Once you have the image stream, you could save it to the filesystem using flutter's file reading & writing capabilities (this is a good resource to learn more about that) - you also need to use a plugin called PathProvider which gets the right path for both iOS and Android which is described in that link. You'd also want to keep track of all the images you'd downloaded and probably delete them after certain amount of time. You'd also have to read the files back before using them to create Image widgets.

That gives you lots of control, but is a bit of work (although not a crazy amount, but if you're new to flutter maybe not something you want to do just now depending on why you want to save the images).

The simple answer is Packages to the rescue! Someone else has already come across this problem and written a plugin that solves it for you, so you don't have to think about it!

See cached_network_image package for information about the plugin.

You need to add to your dependencies in pubspec.yaml

dependencies:
  cached_network_image: "^0.3.0"

Import it:

import 'package:cached_network_image/cached_network_image.dart';

And use it!

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

Note that this downloads & shows the image - if you want to do those seperately you can use a new CachedNetworkImageProvider(url) and show it using new Image.



回答2:

After wandering around the codes and flutter docs. I have found the methods and classes which would work for both iOS and Android and here it goes.

Helper Classs

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));
  }
}

Usage of class

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),
                  ],
            ),
          );
   }
}


回答3:

If all you want is to save an image (example: a .png) to the device, you can easily achieve this with a simple get (http/http.dart) and a File (dart:io).

To do so, you can base yourself in the example below:

var response = await get(imgUrl);
documentDirectory = await getApplicationDocumentsDirectory();

File file = new File(
  join(documentDirectory.path, 'imagetest.png')
);

file.writeAsBytesSync(response.bodyBytes);

Note that in the case above I’ve used the ‘path_provider’ package from the dart pub. In overall you should have in your import at least these items:

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


回答4:

To save the network image in local system you need to use ImagePickerSave dart plugin. Add the dart plugin in pub.yaml file: image_picker_saver: ^0.1.0 and call below code to save the image. URL is the image URL of network image

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));
}


回答5:

You can use image_downloader.

  • For ios, image is saved in Photo Library.
  • For Android, image is saved in Environment.DIRECTORY_DOWNLOADS or specified location. By calling inExternalFilesDir(), specification of permission becomes unnecessary.
  • By callback(), you can get progress status.

The following is the simplest example. It will be saved.

await ImageDownloader.downloadImage(url);


回答6:

I had a lot of trouble doing this, so I wanted to expound a little on the above answers with a simple example that downloads a file on startup, saves it to a local directory. I marked a couple of lines with the comment //%%% to show lines that can be commented out the second time that the app runs. (because it doesn't need to be downloaded to be displayed... it's already on the device itself:

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,
      );
    }
  }
}

And heres my pubspec.yaml file:

http: ^0.12.0+2
  path_provider: 1.5.0


回答7:

Follow this url flutter save network image.

Code Snippet

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();
}
}


回答8:

watch this Download and Resize an Image



标签: dart flutter