I want to export a save file so the user can later import it into the app again later, but how do I allow exporting to a directory outside the app dir (/Android/com.app/files)
if (!(await Permission.storage.request().isGranted)) return;
final String? exportPath = await FilePicker.platform.getDirectoryPath();
if (exportPath == null) return;
File exportFile = await File(
"$exportPath/zs_tracker_data.sav",
).create(recursive: true);
This code is backed by these gradle properties:
android.useAndroidX=true
android.enableJetifier=true
With compileSdkVersion 33 selected, and these permissions:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
All of these above allows me to get a path, and then get this prompt:
However, even when I press 'Allow', I am not allowed to touch that directory and get: Unhandled Exception: FileSystemException: Cannot create file, path = '/storage/emulated/0/Backups/zs_tracker_data.sav' (OS Error: Operation not permitted, errno = 1)
I've considered allowing MANAGE_EXTERNAL_STORAGE, but then my app won't be allowed on Play Store, since the only reasons it needs storage permissions are for creating backups and importing backups. I've also seen this post: Flutter read/write to external storage with Android SDK 30+, but the answer blows over my head completely and seems horribly over complex for my simple backup export/importing...
How do I go about this in a decently simple way? I just want to be able to save the exported file somewhere where the user can easily find it
Maybe use a totally different approach which doesn't require any permissions. Use sharing to allow a user to choose whatever a destination she/he desires. For example using share_plus plugin:
Share.shareFiles(['<path to file>'], text: 'Export file');
Related
I tried to change path of the internal storage file but it could not happen.
Then I tried copy that file to another location and delete it from first location. unfortunately, this also did not work.
Here is my error:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled
Exception: FileSystemException: Cannot delete file, path =
'/storage/emulated/0/DCIM/Screenshots/Screenshot_20230116-114448.jpg'
(OS Error: No such file or directory, errno = 2)
If anyone have idea then it could be helpful. Thank You.
before accessing the storage ask for storage permission with the below code and also you can check this permission package https://pub.dev/packages/permission_handler. Don't forget to add permission in for android and iOS in their respective folder
await Permission.storage.request();
and if you have any other doubt then you can ask it in comments.
If you are in android 10 and above WRITE_EXTERNAL_STORAGE is remove in android native...we can only delete the file that app have write in DCIM or any public directory, if we uninstall the app and reinstall the same app then it won't delete it...
You can view the answer and comment of this stackoverflow
I made an APK with the help of KivyApp. APK generated successfully and my APK also runs on my mobile. The problem is that I didn't give any specific path for the CSV file, which stored the output generated by the app. Initially, I run python code in the pydroid3 app and it automatically generated the CSV file at the same location, where my code was stored. My question is if I want to store the data in the internal storage of my mobile, what path should I enter?
import csv
csvfile = "Discrete_pos.csv"
with open(csvfile, "a") as fp:
wr = csv.writer(fp, dialect='excel')
wr.writerow(csvRow)
Well, I suppose you mean the path of External Storage (which can be accessed by any app, one you see in your mobile file explorer) As it's already storing in your internal storage path. Now to access external storage there are 2 methods. But first of all you need storage permission for that. To get storage permission use the following code:
from android.permissions import request_permissions, Permission
request_permissions([Permission.WRITE_EXTERNAL_STORAGE, Permission.READ_EXTERNAL_STORAGE])
Now after you have access to external storage you can use the following code to get external storage path:
from android.storage import primary_external_storage_path
external_storage = primary_external_storage_path()
Sometimes this doesn't work on some android devices so if you are getting that issue then you can also use os.getenv:
external_storage = os.getenv('EXTERNAL_STORAGE')
Also, don't forget to write READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE in the permissions of your buildozer.spec file
After a lot of googling, and a lot of tries with "out-of-context" code, I'm begging you to help me.
I'm trying to figure out if it's possible to write on the external storage with Nativescript. I'm not interested to write within the application context. So what the docs shows, it's not what i'm looking for.
I've managed to achieve this from a thread on the Nativescript forum:
android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString();
It works, it gives me a path, but when I have this path I have no clue of what to do with it. How to create a file inside that path, read it etc.
What I need to achieve is to create a folder that both the user and the application can easily access. The user should be able to access this folder with the builtin files explorer.
The application runs on Angular.
I really struggled with this one on Android device and in the end it was due to:
Not making sure the required permissions has been granted by the user in the app
Using "Android File Transfer" on my Macbook to verify the files have been created and to download them
tns info
nativescript 6.0.3
tns-core-modules 6.0.7
tns-android 6.0.2
tns plugins
nativescript-permissions 1.3.7
example code
import * as fs from "tns-core-modules/file-system"
...
// First get the required permissions
// Note: this permissions should also be in your AndroidManifest.xml file as:
// <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
const permissions = require('nativescript-permissions')
permissions.requestPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
.then(() => {
console.log('Required Android permissions have been granted');
})
.catch(() => {
console.error('Required Android permissions have been denied!');
});
// Get the publicly accessable Downloads directory path
const sdDownloadPath = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString()
console.log('sdDownloadPath: ' + sdDownloadPath)
// Get a specific folder in that path (will be created if it does not exist)
const myAppFolder = fs.Folder.fromPath(fs.path.join(sdDownloadPath, 'myApp'))
console.log('myApp path: ' + myAppFolder.path)
// Get a file in that path (will be created if it does not exist)
// Note: In this case we try to get a unique file every time this code is run
let date = new Date()
date = date.toISOString().replace('.', '')
const myFile = myAppFolder.getFile(`myfile_${date}.txt`)
console.log('myFile path: ' + myFile.path)
// Write some data to this new file
myFile.writeText('Hello duder 123')
.then(() => {})
.catch((err) => console.log(`Error writing to file: ${err}`))
// Try and read back the data that was written
myFile.readText()
.then((res) => {
console.log(`Text read back: ${res}`)
}).catch((err) => {
console.log(err.stack);
});
// List all files in the myApp folder
myAppFolder.getEntities()
.then((entities) => {
// entities is array with the document's files and folders.
entities.forEach((entity) => {
console.log(entity)
});
}).catch((err) => {
console.log(err.stack);
});
android file transfer issue
One problem I wasted a lot of time on was that I could see the files with getEntities() but could not see them when using the 3rd party tool "Android File Transfer (AFT)" on Mac. I eventually stumbled across "Android Studio's -> Device File Explorer" and could see all my created files and folders with it so realised the issue is with AFT.
I now make use of Airdroid to browse and download device files.
applicable reference
https://docs.nativescript.org/angular/ng-framework-modules/file-system
(angular docs but relevant to nativescript-vue as well)
Do you know your external(sd card) path?
If it is like /storage/emulated/0, then you could try this to create a folder or file.
import * as fs from "tns-core-modules/file-system";
let externalPath= fs.path.join(android.os.Environment.getExternalStorageDirectory().getAbsolutePath().toString());
//Create a folder with known path
var folder: fs.Folder = fs.Folder.fromPath(sdCardPath+"/test");
//Create a file
var testFile: fs.File = folder.getFile("test.txt");
console.log("Path " + folder.path)
User should be able to access this fold and file. It is in device internal storage which is "external" folder.
I still try to figure out how to get access to sd card but hope above code work for you.
I have the same issue and finally solved it by adding android:requestLegacyExternalStorage="true" inside the AndroidManifest.xml file
follow the thread here
I have a hybrid app in production (iOS/Android) that uses Cordova plugins. I have one Android user who is getting a SECURITY_ERR when using the File plugin. This seems to be happening on a call to writeFile(). The file path I'm writing to for Android is externalRootDirectory.
Can anyone help me understand why 1 user (out of 300-400) would have this problem? The user is on Android 6.0.1 if that helps.
Some code is below. The error I'm getting is [Error creating file] – Error Msg: [SECURITY_ERR], so the .writeFile() catch is being hit in this case.
//Handle Native download
if (this.appConfig.isNative) {
this.loggingService.debug("Starting to create native file");
//Get base file path for android/ios
let filePath = (this.appConfig.isNativeAndroid) ? this.file.externalRootDirectory : this.file.cacheDirectory;
//Write the file
this.file.writeFile(filePath, fileName, data, { replace: true })
.then((fileEntry: FileEntry) => {
this.loggingService.debug("Created file: " + fileEntry.toURL());
//Open with File Opener plugin
this.fileOpener.open(fileEntry.toURL(), data.type)
.then(() => this.loggingService.debug('File is opened'))
.catch(e => this.loggingService.error('Error openening file', e));
})
.catch((err) => {
this.loggingService.error("Error creating file", err);
throw err; //Rethrow - will be caught by caller
});
}
I was able to figure this one out. Looks like after Android 6.0 certain permissions must be requested during use of the app (not just at install time). This is also alluded to in the Cordova File plugin docs, under Android Quirks.
Marshmallow requires the apps to ask for permissions when reading/writing to external locations. By default, your app has permission to write to cordova.file.applicationStorageDirectory and cordova.file.externalApplicationStorageDirectory, and the plugin doesn't request permission for these two directories unless external storage is not mounted. However due to a limitation, when external storage is not mounted, it would ask for permission to write to cordova.file.externalApplicationStorageDirectory.
So on Android 6.0+, when writing a file to disk the Cordova File plugin will display a prompt similar to:
Allow APP_NAME to access photos, media and files on your device?
If the user selects Deny to this request, then the Cordova file write will get this SECURITY_ERR, even if the app requests this permission at install time.
I'm trying to use expansion files and I found an issue that I'm not able to resolve.
It seems that you can access the /Android/obb folder and eventually delete the /Android/obb/my.package.name directory (tried with some file manager), but I cannot handle this situation whithin the app.
If I just let the Google Downloader library try to download the expansion file it will hang and error (for the missing folder), but it will start if I recreate the folder.
The strange thing is that I'm able to create the folder from other apps (the same file manager that I've used to see the directory) but not from mine!
I tried with
File f = new File(Environment.getExternalStorageDirectory(), "Android/obb/my.package.name");
f.mkdirs();
but it doesn't work.
I've the permissions in the manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
and I'm targeting api 23 and compiling with the 25.
OP dropped the Target SDK to 22 and was able to avoid Dangerous permission work flow which was causing the issue.
I had this same problem, but lowering the Target SDK was not an option.
The Context::getObbDir() method allows access to the expansion folder without the usual security rules. I found that, if the folder doesn't exist, getObbDir() creates it too (You can also double check and create it manually with mkdir()).
Excerpt from the documentation linked above:
Return the primary shared/external storage directory where this application's OBB files (if there are any) can be found. Note if the application does not have any OBB files, this directory may not exist.
This is like getFilesDir() in that these files will be deleted when the application is uninstalled, however there are some important differences:
... Starting in Build.VERSION_CODES.KITKAT, no permissions are required to read or write to the path that this method returns. ...
Starting from Build.VERSION_CODES.N, Manifest.permission.READ_EXTERNAL_STORAGE permission is not required, so don’t ask for this permission at runtime. ...
So you can do something like:
File obbDir = getObbDir();
if (null == obbDir) {
// Storage is not available
} else if (!obbDir.exists() && !obbDir.mkdir()) {
// Failed to create directory. Shouldn't happen but you never know.
}
NOTE: You may need the read/write permissions to access the expansion files within the folder.