3

I am trying to save the image from assets to internal storage. But, I could not load the image from assets to file. Here is what I have done:

onTap: () async {
  
  final String documentPath = (await getApplicationDocumentsDirectory()).path;
  String imgPath = (galleryItems[currentIndex].assetName).substring(7);
  File image = await getImageFileFromAssets(imgPath);

  print(image.path);
}

I used substring(7) to eliminate assets/ as, my assetName comes as assets/images/foo.jpg.

Future<File> getImageFileFromAssets(String path) async {
  final byteData = await rootBundle.load('assets/$path');

  final file =
      await File('${(await getApplicationDocumentsDirectory()).path}/$path')
          .create(recursive: true);
  await file.writeAsBytes(byteData.buffer
      .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));

  return file;
}

After I get image, I don't know how to proceed forward to create a directory with my name in internal storage. And, copy file there.

*Note:- I have editted the post, as some basic mistakes were pointed out.

Update

Here is what I came up with. And it saves image in the /storage/emulated/0/Android/data/com.example.my_project/files/Pics/foo.jpg path.

File image = await getImageFileFromAssets(imgPath);

final extDir = await getExternalStorageDirectory();

// Path of file
final myImagePath = '${extDir.path}/Pics';

// Create directory inside where file will be saved
await new Directory(myImagePath).create();

// File copied to ext directory.
File newImage =
    await image.copy("$myImagePath/${basename(imgPath)}");

print(newImage.path);

Here are some links that really helped me:

Special thanks to @David for the help. Please see comments to understand full scene if you are here to solve your similar problem.

So, I am accepting the answer from @David.

Biplove Lamichhane
  • 3,995
  • 4
  • 14
  • 30
  • 1
    now the error must have changed? please post it – Yadu Aug 06 '20 at 04:10
  • 1
    No, now there is not any error. What error are you expecting??? It throws exception ``FileSystemException: Cannot open file, path = '/data/user/0/com.example.pig_salang/app_flutter/images/foo.jpg' (OS Error: No such file or directory, errno = 2)`` if I remove ``.create(recursive: true)``. – Biplove Lamichhane Aug 06 '20 at 04:18
  • so you need to write the file to a folder in the root storage which is available to anyone using the device? or you just need a safe place on the device's internal storage? – Yadu Aug 06 '20 at 04:25
  • In the root storage... So, that user could get the files from assets to there gallery. ``.create()`` removed. – Biplove Lamichhane Aug 06 '20 at 04:27
  • I'm no longer clear what the problem is, but if you want the file to be accessible by the user, you should use `getExternalStorageDirectory` – David Aug 06 '20 at 04:30
  • I ll look on that – Biplove Lamichhane Aug 06 '20 at 04:45

2 Answers2

3

You are trying to create a file object in a path that doesn't exist. You're using your asset path, the path relative to the root of your Flutter project. However, this path doesn't exist in the device's documents folder, so the file can't be created there. The file also doesn't exist in the assets folder because you're prepending the documents path.

To fix your issue, you should pass assetName to rootBundle.load(), without the documents path and open the File() somewhere like $documentPath/foo.jpg

Edit: To create the file you still have to call File.create, so you need to run:

final file = await File('$documentPath/images/foo.jpg').create(recursive: true);
David
  • 1,688
  • 1
  • 11
  • 21
  • 1
    Then, I get exception of ``FileSystemException: Cannot open file, path = '/data/user/0/com.example.pig_salang/app_flutter/images/foo.jpg' (OS Error: No such file or directory, errno = 2)`` – Biplove Lamichhane Aug 06 '20 at 03:19
  • Yes, that's because you added the `images` folder. I meant specifically just `foo.jpg`, otherwise you have to create the directory as explained in the docs: https://api.flutter.dev/flutter/dart-io/Directory-class.html – David Aug 06 '20 at 03:24
  • 1
    Sorry, my mistake, forgot to actually create the file. Then you can just use the `recursive` property, instead of creating the directory in a separate step. I've edited my answer. – David Aug 06 '20 at 03:32
  • Okay... I ll look at it. – Biplove Lamichhane Aug 06 '20 at 03:52
  • 1
    why are you giving it a external directory? do `rootBundle.load('assets/somefolder/somefile.png')` as you are accessing a assets bundled with your app you have to look for it in assets root – Yadu Aug 06 '20 at 03:57
  • Please review post, as I made changes. – Biplove Lamichhane Aug 06 '20 at 04:17
2

For future reference, this is just to update the solution of @Biplove which really helped me a lot as a newbie...

Future<File> getImageFileFromAssets(String unique, String filename) async {
  ByteData byteData = await rootBundle.load(assets/filename));

  // this creates the file image
  File file = await File('$imagesAppDirectory/$filename').create(recursive: true); 

  // copies data byte by byte
  await file.writeAsBytes(byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
  
  return file;
}

Thanks.

damiro
  • 21
  • 1
  • 3
  • Is there a drawback using `image.copy` or why do you chose using a byte by byte copy instead? – w461 Aug 17 '21 at 10:19
  • 1
    at that time, this is the best solution I could find. The situation is that I need to copy an Image from assets to App directory without displaying it. To answer your question, I don't think there will be draw backs if you can make if work by the use of `image.copy`. – damiro Sep 08 '21 at 05:58