Flutter/Dart: Saving an image file to "/storage/emulated/0/Picture/AppFolder/" - flutter

using
final dir = await getExternalStorageDirectory();
Image get saved in
/storage/emulated/0/Android/data/com.example.flutter_app/files/
But I want to store it in ../0/Pictures/app_name/ so that it shows up in the gallery.
I looked up all over the www and couldn't figure out. Please advise.

You have to first extract the root Path from the returned location
rootPath = /storage/emulated/0/
than create the Pictures and app_name Directory (to avoid exception when the directory doesn't exist)
then save file in /storage/emulated/0/Pictures/app_name/
here's a simple example to help you understand:
...
Directory externalPath = (await getExternalStorageDirectory());
String picturesDirName = "Pictures";
String appNameDirName = "app_name";
// Splitting the externalPath
List<String> externalPathList = externalPath.path.split('/');
// getting Position of 'Android'
int posOfAndroidDir = externalPathList.indexOf('Android');
//Joining the List<Strings> to generate the rootPath with "/" at the end.
String rootPath = externalPathList.sublist(0, posOfAndroidDir).join('/');
rootPath+="/";
//Creating Pictures Directory (if not exist)
Directory picturesDir = Directory(rootPath+picturesDirName+"/");
if (!picturesDir.existsSync()) {
//Creating Directory
await picturesDir.create(recursive: true);
//Directory Created
} else {
//Directory Already Existed
}
//Creating "app_name" Directory (if not exist)
Directory appNameDir = Directory(rootPath+picturesDirName+"/"+appNameDirName+"/");
if (!appNameDir.existsSync()) {
//Creating Directory
await appNameDir.create(recursive: true);
//Directory Created
} else {
//Directory Already Existed
}
//Creating String varible to store the path where you want to save file.
String fileSaveLocation = rootPath+picturesDirName+"/"+appNameDirName+"/";
// Or you can also use templates like this
String fileSaveLocation2 = "$rootPath$picturesDirName/$appNameDirName/";
//Your File Path where you want to save you file.
String filePath = fileSaveLocation+"text.txt";
// Or you can also use templates like this
String filePath2 = "${fileSaveLocation2}test.txt";
...
You can optimize the above code as per your liking.
hope this is the solution you were looking.

Here, Is how you can acheive this,
final Directory extDir = await getExternalStorageDirectory();
String dirPath = '${extDir.path}/app_name/pictures';
dirPath = dirPath.replaceAll("Android/data/com.example.flutter_app/files/", "");
await Directory(dirPath).create(recursive: true);
// start File Operations Here with dirPath variable

Update:
Along with detailed answer below, I came across a couple of plugins which deals specially with media content.
From Flutter.dev docs:
The plugin currently supports access to two file system locations:
gallery_saver Plugin: See Full Example
GallerySaver.saveImage(path).then((bool success) {// code }
Image_gallery_saver: See Full Example
await ImageGallerySaver.saveImage(Uint8List.fromList(response.data), quality: 60, name: "hello");
Creating the path manually: Source
await Permission.storage.request();
if (await Permission.storage.isGranted) {
var dir = await getExternalStorageDirectory();
Dio dio = Dio();
var newPath = "";
List<String> paths = dir.path.split("/");
for (int x = 1; x < paths.length; x++) {
String folder = paths[x];
if (folder != "Android") {
newPath += "/" + folder;
}
else {
break;
}
}
var picfolder = "/Pictures/";
newPath = newPath + picfolder + AppStrings['AppName'];
Other Useful References:
Android Data Storage
MediaStore in Flutter

Related

How do I make a separate folder for my app and store images there? Flutter

Hey guys so I wish to make a new folder for my app under Android and then store images there which will be retrieved for future use.
Here's what I have tried -
Future<bool> LocalImage(String url) async {
Directory directory;
var dio = Dio();
try {
if (Platform.isAndroid) {
if (await _requestPermission(Permission.storage) &&
// access media location needed for android 10/Q
await _requestPermission(Permission.accessMediaLocation) &&
// manage external storage needed for android 11/R
await _requestPermission(Permission.manageExternalStorage)) {
directory = (await getExternalStorageDirectory()) as Directory;
String newPath = "";
print(directory);
List<String> paths = directory.path.split("/");
for (int x = 1; x < paths.length; x++) {
String folder = paths[x];
if (folder != "Android") {
newPath += "/" + folder;
} else {
break;
}
}
newPath = newPath + "/Verif-ID";
if (await Directory(newPath).exists()){
;
}
else{ directory = Directory(newPath);
}
} else {
return false;
}
} else {
if (await _requestPermission(Permission.photos)) {
directory = await getTemporaryDirectory();
} else {
return false;
}
}
if (await directory.exists()) {
File saveFile = File(directory.path );
await dio.download(url, saveFile.path,);
return true;
}
} catch (e) {
print(e);
}
return false;
}
The issues I am currently facing are-
1.It does make a new folder by that name but no download is happening
2. I keep getting the following in my console
FileSystemException: Cannot create file, path = '/storage/emulated/0/Android/data/com.example.id_me/files' (OS Error: Is a directory, errno = 21)
I understand that this error must mean that a folder of the name already exists but I thought my if conditions were checking that but I guess not. My logics are not the best haha.
This is my first flutter app so I am very sorry if this was a silly doubt.
The URL will be from my firebase database.
Add a file name to a directory path.
savePath: The path to save the downloading file later. A path with String type, eg "xs.jpg"
await dio.download(url, saveFile.path + "/xs.jpg")
See download for details.

Download a sub stream with youtube_explode_dart

I'm developing a Flutter app, using the package youtube_explode_dart.
Currently I download all the audio stream, then crop it using ffmpeg. Quickly this looks like :
var id = "aboZctrHfK8";
var file = File("mySong.mp4");
var start = "2000ms";
var end = "5000ms";
// Downloading audio only
var manifest = await yt.videos.streamsClient.getManifest(id);
var audio = manifest.audioOnly.first;
var audioStream = yt.videos.streamsClient.get(audio);
var output = file.openWrite(mode: FileMode.writeOnlyAppend);
for (final data in audioStream) {
output.add(data);
}
// Croping audio
FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
await _flutterFFmpeg.executeWithArguments([
"-v", "error",
"-ss", start,
"-to", end,
"-i", file.path,
"-acodec", "copy", "myCroppedSong.mp4"]);
Now I'm facing another issue: Some videos are really heavy and take a while to download. This is not acceptable for my end users, especially since I only want part of the original audio.
Is there a way to download only a subset of the audio stream?
Something like:
for (final data in audioStream.subset(start, end)) {
output.add(data);
}
It would be anwsome!
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
// Initialize the YoutubeExplode instance.
final yt = YoutubeExplode();
Future<void> ExplodeDown() async {
stdout.writeln('Type the video id or url: ');
var url = stdin.readLineSync()!.trim();
// Save the video to the download directory.
Directory('downloads').createSync();
// Download the video.
await download(url);
yt.close();
exit(0);
}
Future<void> download(String id) async {
// Get video metadata.
var video = await yt.videos.get(id);
// Get the video manifest.
var manifest = await yt.videos.streamsClient.getManifest(id);
var streams = manifest.videoOnly;
// Get the audio track with the highest bitrate.
var audio = streams.first;
var audioStream = yt.videos.streamsClient.get(audio);
// Compose the file name removing the unallowed characters in windows.
var fileName = '${video.title}.${audio.container.name}'
.replaceAll(r'\', '')
.replaceAll('/', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
var file = File('downloads/$fileName');
// Delete the file if exists.
if (file.existsSync()) {
file.deleteSync();
}
// Open the file in writeAppend.
var output = file.openWrite(mode: FileMode.writeOnlyAppend);
// Track the file download status.
var len = audio.size.totalBytes;
var count = 0;
// Create the message and set the cursor position.
var msg = 'Downloading ${video.title}.${audio.container.name}';
stdout.writeln(msg);
// Listen for data received.
// var progressBar = ProgressBar();
await for (final data in audioStream) {
// Keep track of the current downloaded data.
count += data.length;
// Calculate the current progress.
var progress = ((count / len) * 100).ceil();
print (progress);
// Update the progressbar.
// progressBar.update(progress);
// Write to file.
output.add(data);
}
await output.close();
}
**How to download load youtube video & audio & play stream audio **
String youTubeLink = "https://www.youtube.com/watch?v=Ja-85lFDSEM";
Future<void> _downloadVideo(youTubeLink) async{
final yt = YoutubeExplode();
final video = await yt.videos.get(youTubeLink);
// Get the video manifest.
final manifest = await yt.videos.streamsClient.getManifest(youTubeLink);
final streams = manifest.muxed;
final audio = streams.first;
final audioStream = yt.videos.streamsClient.get(audio);
final fileName = '${video.title}.${audio.container.name.toString()}'
.replaceAll(r'\', '')
.replaceAll('/', '')
.replaceAll('*', '')
.replaceAll('?', '')
.replaceAll('"', '')
.replaceAll('<', '')
.replaceAll('>', '')
.replaceAll('|', '');
final dir = await getApplicationDocumentsDirectory();
final path = dir.path;
final directory = Directory('$path/video/');
await directory.create(recursive: true);
final file = File('$path/video/$fileName');
final output = file.openWrite(mode: FileMode.writeOnlyAppend);
var len = audio.size.totalBytes;
var count = 0;
var msg = 'Downloading ${video.title}.${audio.container.name}';
stdout.writeln(msg);
await for (final data in audioStream){
count += data.length;
var progress = ((count / len) * 100).ceil();
print(progress);
output.add(data);
}
await output.flush();
await output.close();
}

Flutter Image gallery saver - how to access path

I'm using Image gallery saver plugin for saving images. Method
await ImageGallerySaver.saveImage(pngBytes)
returns an object
{filePath: file:///storage/emulated/0/wallpapers/1608205629471.jpg, errorMessage: null, isSuccess: true}
I'd like to get the path of the newly saved file (/storage/emulated/0/wallpapers/1608205629471.jpg).
Is there a way to achieve that?
That is a Map object. Access the filePath String like so:
var response = await ImageGallerySaver.saveImage(pngBytes);
// value = json['key']
var path = response['filePath']; // 'file:///storage/emulated/0/wallpapers/1608205629471.jpg'
you can access the key from a map like this
final imagePath = result['filePath'].toString();
then if you need to get a location path removing file:// with regular expression
final imagePath = result['filePath'].toString().replaceAll(RegExp('file://'), '');
read documentation for more info
First You need to same the image id on database that you wants to retrieve.
Then pass the id with this method I am giving a snippet that I have used:
import 'package:path/path.dart' as p;
Directory _appDocsDir;
class ImageCashUtil {
ImageCashUtil() {
init();
}
init() async {
WidgetsFlutterBinding.ensureInitialized();
_appDocsDir = await getApplicationDocumentsDirectory();
return _appDocsDir;
}
File fileFromDocsDir(String filename) {
String pathName = p.join(_appDocsDir.path, filename);
return File(pathName);
}
}

How to get the File Extension from a string Path

I've got file path saved in variable and I want to get the file type extension by using path package https://pub.dev/packages/path So far I managed to do it by splitting the string like this
final path = "/some/path/to/file/file.dart";
print(path.split(".").last); //prints dart
Is there any way to achieve this with path package?
You can use the extension function in the path package to get the extension from a file path:
import 'package:path/path.dart' as p;
final path = '/some/path/to/file/file.dart';
final extension = p.extension(path); // '.dart'
If your file has multiple extensions, like file.dart.js, you can specify the optional level parameter:
final extension = p.extension('file.dart.js', 2); // '.dart.js'
No need of any extension. You can try below code snippet.
String getFileExtension(String fileName) {
try {
return "." + fileName.split('.').last;
} catch(e){
return null;
}
}
This small function can parse filepath or url and find basename, extension and absolute path. It doesn't check file path exist and not check basename is folder or file.
Map parsePath(String filepath) {
Map p1 = new Map();
int ind1 = filepath.indexOf("://");
if (ind1 > 0) {
p1["fullpath"] = filepath;
} else {
p1["fullpath"] = File(filepath).absolute.path;
}
p1["path"] = filepath;
List<String> v = filepath.split("/");
if (v.length > 1) {
p1["basename"] = v.last;
} else if (filepath.split("\\").length > 1) {
p1["basename"] = filepath.split("\\").last;
} else {
p1["basename"] = v.last;
}
p1["extension"] = p1["basename"].split('.').last;
if (p1["basename"] == p1["extension"]) p1["extension"] = "";
return p1;
}

Flutter:Dart unzip & save response from http

i need help for this:
Using Flutter 1.12.13+hotfix.5 & Dart 2.7.0
i receive a zip response.bodyBytes from an http.get
how can i zipdecode and save directly to disk ?
This an extract of my code to show what i'm searching:
import 'dart:async';
import 'dart:io';
...
var basicAuth = 'my auth';
Map<String, String> headers = new HashMap();
headers.putIfAbsent('authorization', () => basicAuth);
headers.putIfAbsent('Content-Type', () => 'application/json');
var url = 'http://myhost';
http.Response response = await http.get(
url,
headers: headers,
);
var dir = await getApplicationDocumentsDirectory();
String path = dir.path + '/';
String fn = 'filename.xxx';
new File(path + fn).writeAsBytes(response.bodyBytes); // This cmd store zip file to disk
// In my case i've a zip file with only one file inside, below a code to unzip all files inside zip
// Extract the contents of the Zip archive to disk.
for (ArchiveFile file in archive) {
String filename = file.name;
if (file.isFile) {
List<int> data = file.content;
File('out/' + filename)
..createSync(recursive: true)
..writeAsBytesSync(data);
} else {
Directory('out/' + filename)
..create(recursive: true);
}
}
// Instead i'd like to unzip the only one file there's inside
IF your data is in gzip format you can try
GZipCodec().decode(response.bodyBytes);