Android write file on External SD Card for android 5+ - android

I am unable to write file to external SD Card . I get error message EAcess denied. I have searched a lot on internet and found that from Android 4.4 + android's Storage Access Framwork (SAF) is required to write file.
But I am using some android applications which are able to write(Create/Delete/Rename) file on SD Cards. They are not using SAF.
So please help me as to how can I do this without using SAF framwork.
Thanks

There are a lot of confusions talking about External Memory of Android. It doesn't point to Removable SD MICRO CARD actually. So, what Google thinks "external memory" means
Refer to Android API Document
Every Android-compatible device supports a shared "external storage"
that you can use to save files. This can be a removable storage media
(such as an SD card) or an internal (non-removable) storage. Files
saved to the external storage are world-readable and can be modified
by the user when they enable USB mass storage to transfer files on a
computer.
The fact is Environment.getExternalStorageDirectory() and Context.getExternalFilesDirs() could return an emulated External Memory located inside the Internal storage. Thus, these functions themselves don't give an expected results. The SECONDARY_STORAGE environment variable can help to get a real path of removable memory but writing on root of this isn't allowed because of OEM implementation. In this case, we should try to get app's data folder by Context.getExternalFilesDirs() or ContextCompat.getExternalFilesDirs() on which app's data file is allowed to be read and written.
I solve my problem by using below method, please check it and hope it helps you overcome your issues.
#TargetApi(Build.VERSION_CODES.KITKAT)
private String getRemovablePath(){
String secondaryStore = System.getenv("SECONDARY_STORAGE");
if (secondaryStore != null){
secondaryStore = secondaryStore.split(":")[0];
secondaryStore += File.separator + "Backups/";
File file = new File(secondaryStore);
if((file.mkdir() || file.isDirectory()) && isFileWritable(secondaryStore)){
return secondaryStore;
} else {
secondaryStore = null;
}
}
// try again by fix address
if(secondaryStore == null){
if (new File("/Removable/MicroSD/").exists()){
secondaryStore = "/Removable/MicroSD/";
} else if( new File("/storage/extSdCard/").exists()){
secondaryStore = "/storage/extSdCard/";
} else if( new File("/storage/sdcard1/").exists()){
secondaryStore = "/storage/sdcard1/";
} else if( new File("/storage/usbcard1/").exists()){
secondaryStore = "/storage/usbcard1/";
} else if( new File("/storage/external_SD/").exists()){
secondaryStore = "/storage/external_SD/";
}
/** add more fix addresses you know */
secondaryStore += "Backups/";
File file = new File(secondaryStore);
if((file.mkdir() || file.isDirectory()) && isFileWritable(secondaryStore)){
return secondaryStore;
} else {
secondaryStore = null;
}
}
/** Try data folder*/
if(secondaryStore == null){
int ver = Build.VERSION.SDK_INT;
File[] externalRoots = null;
if(ver <= Build.VERSION_CODES.JELLY_BEAN_MR2){
externalRoots = ContextCompat.getExternalFilesDirs(getBaseContext(), null);
} else {
externalRoots = getExternalFilesDirs(null);
}
if(externalRoots.length > 1){
secondaryStore = externalRoots[1].getAbsolutePath() + File.separator;
return secondaryStore;
} else {
secondaryStore = null;
}
}
return secondaryStore;
}

please check the link, where present issue:
issue
for access to external memory in previous android versions there is no problem. current possess improvements

Android API < 23
Your Android Manifest must declare the specific user permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
You also have to declare the reading permission if you also intend to read files:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
User permissions must be placed before the application section, like this:
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
<application>
Source of my explanation and examples on how to save files can be found in the official documentation.
Android 6 (API 23)
Things get a bit different starting with Android API 23 because user permissions have to be asked to the user in runtime when needed. A valid answer to this was already given here.

SAF is only needed if you have to write to any location on the SD Card. To write to your app-specific directory on the SD Card, you can use context.getExternalFilesDirs. One of the paths returned will be the path of the your app specific folder on the SD Card.
Again, this is manufacturer dependent as well. If the manufacturer has not set the SECONDARY_STORAGE environment variable, the paths returned by getExternalFilesDirs will not contain the SD Card path.

Related

Android mkdir() can't make folder inside root of removable micro SD-card

I use this code to get folder of removable SD-card:
var baseDir = Environment.getExternalStorageDirectory().absolutePath
val dirs = getExternalFilesDirs(null)
for (file in dirs){
if(Environment.isExternalStorageRemovable(file)) {
baseDir = file.absolutePath
val idx = baseDir.indexOf("/Android/")
if(idx > 0) {
baseDir = baseDir.substring(0,idx)
break
}
}
}
And it work well enough, at least on test devices. After this code baseDir contain something like /storage/AB96-CD85. But when I try to make dir, I always get false result:
baseDir = baseDir + java.io.File.separator + name
var result: File? = null
if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
Log.e(TAG, "SD-card not mounted.")
return result
}
val dir = File(baseDir)
if (!dir.exists()) {
if (!dir.mkdir()) {
Log.e(
TAG,
"Can't create directory $name"
)
return result
}
}
minSdkVersion is 21. Before this code execution I've already requested WRITE_EXTERNAL_STORAGE permission and got positive response from user. I wrote permission block in Manifest in correct place (out of <application></application> block). The code above create folder without any problems on one device and cannot do the same on other (in the same time ES File manager work well on sdcard). What was tried:
App re-installation
Phone restart
Even unplag USB cable
Storage Access Framework(SAF)
What I should do to make mkdir() function work on the second device?
If I should add some other permission or move user to some system settings screen, how can I do that?
Why code above work on one device and doesn't work on other?
I think it's about different Android versions.
To modify External SD card's filesystem, you need to get "special permision".
You can Google some libraries, that do it automatically. Just use one of these and you will gain that "special permission". It's weird but WRITE_EXTERNAL_STORAGE not providing you access to SD Card, only to eMMC memory.

getExternalFilesDir () doesnot store file to mounted memory card

I am using Nougut 7.1.1 device
When I run the below given code, file is stored in device or internal storage "emulated/0..." but I want to store it in removable memory card.
I logged getExternalStorageState() and it shows mounted.
I tried using Environment.getExternalStorageDirectory() also,stored in "emulated/0...", no result
I have used permissions in manifest file too as below:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Note: However, both internal and external(memory card) storage consist of "Hello World" folder like:
Android/Data/com.example.myapplication/files/Hello World/
but stored file(myData2.txt) is present only in "Hello World" folder of internal storage
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("tag", Environment.getExternalStorageState().toString());
Log.i("tag", getExternalFilesDir(null).toString());
savePrivate();
}
public void savePrivate() {
String info = "Written";
File folder = getExternalFilesDir("Hello World");// Folder Name
File myFile = new File(folder, "myData2.txt");// Filename
writeData(myFile, info);
}
private void writeData(File myFile, String data) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(myFile);
fileOutputStream.write(data.getBytes());
Toast.makeText(this, "Done" + myFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Changed my savePrivate() method as below and it worked like a charm!!
As per #CommonsWare suggested,using getExternalFilesDirs() returned available array of locations from which I could select particular storage,In my case folder[0] pointed to "emulated/0.." and folder[1] pointed to removable storage (storage/15C8-119Z/...).
public void savePrivate() {
String info = "Written";
File[] folder = getExternalFilesDirs("backup");// Folder Name
Log.i("tag", String.valueOf(folder[1]).toString());
File myFile = new File(folder[1], "myData2.txt");// Filename
writeData(myFile, info);
}
but stored file(myData2.txt) is present only in "Hello World" folder of internal storage
It is stored in what the Android SDK refers to as external storage. External storage is not removable storage, nor is it what the Android SDK refers to as internal storage.
When I run the below given code, file is stored in device or internal storage "emulated/0..." but I want to store it in removable memory card.
First, you are passing an invalid value to getExternalFilesDir(). Please follow the documentation and pass in a valid value (e.g., Environment.DIRECTORY_MUSIC) or null.
To write to removable storage, replace getExternalFilesDir() with getExternalFilesDirs() and choose a location from the returned array of locations. If that array has 2+ entries, 1+ of them will be on removable storage.
Beginning with Android 4.4 (API level 19), reading or writing files in your app's private external storage directory—accessed using getExternalFilesDir()—does not require the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions. So if your app supports Android 4.3 (API level 18) and lower, and you want to access only the private external storage directory, you should declare that the permission be requested only on the lower versions of Android by adding the maxSdkVersion attribute:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
</manifest>
Select between multiple storage locations
Sometimes, a device that allocates a partition of the internal memory for use as the external storage also provides an SD card slot. This means that the device has two different external storage directories, so you need to select which one to use when writing "private" files to the external storage.
Beginning with Android 4.4 (API level 19), you can access both locations by calling getExternalFilesDirs(), which returns a File array with entries for each storage location. The first entry in the array is considered the primary external storage, and you should use that location unless it's full or unavailable.
If your app supports Android 4.3 and lower, you should use the support library's static method, ContextCompat.getExternalFilesDirs(). This always returns a File array, but if the device is running Android 4.3 and lower, then it contains just one entry for the primary external storage (if there's a second storage location, you cannot access it on Android 4.3 and lower).
Check official documentation for detailed description.
Hope this helps.
This is a function in context where you could get all mounted storage's
ContextCompat.getExternalFilesDirs(context,Environment.YOUR_DIRECTORY);
So , First is your primary internal storage and second will be your mounted sd-card if mounted . You may use this function that returns the sd-card directory if sd-card mounted else the internal-storage directory
File getDir(Context context){
File[] mountedStorage = ContextCompat.getExternalFilesDirs(context,Environment.DIRECTORY_PICTURES);
return mountedStorage[mountedStorage.length-1];
}
With Kotlin
val dir:File = ContextCompat.getExternalFilesDirs(context,Environment.YOUR_DIRECTORY).last()

Unable to save data in SD Card Directory

I have write a code to find if SD card path if available which is like this
File[] paths = ContextCompat.getExternalFilesDirs(context, null);
if (paths.length > 1) {
if (paths[1] != null) {
root = paths[1].getAbsolutePath();
// for sd card
} else {
root = Environment.getExternalStorageDirectory().toString();
}
} else {
root = paths[0].getAbsolutePath();
}
I am saving my data in path "/storage/4130-1912/Android/data/com.enable/files" but I wanted to save data outside Android folder.
I have also tried to make an directory outside the Android folder.But unable to make it.I am testing in Lave phone with version Marhmallow
As you are using Marshmallow, you should be requesting runtime permissions (more details here). Despite that, you may add the WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE for external storage access. You can find more information on how to here.
You can access the external storage path like this: Environment.getExternalStorageDirectory().getAbsoluteFile(). If you are getting FileNotFoundException, it's probably because you didn't add an additional "/" before your file. Example:
File file = new File(Environment.getExternalStorageDirectory().getAbsoluteFile() +
"/your_file.txt");
I am saving my data in path "/storage/4130-1912/Android/data/com.enable/files"
Ok. Go ahead. You will succeed.
but I wanted to save data outside Android folder.
Well that is not possible anymore on modern Android systems.
Even inside the Android folder you can only write to mentioned private directory for your app.
If you want to write to the whole micro SD card then use the storage access framework.
This will help you
As miguelarc said:
As you are using Marshmallow, you should be requesting runtime
permissions (more details here). Despite that, you may add the
WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE for external
storage access.
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + getPackageName() + "/yourdir");
if (f.mkdirs() || f.isDirectory())
Log.e("path",f.getPath());
//do what you want
}

Use Googles FUSE to determine if SD CARD is mounted

We have looked at numerous SO post that deal with the SD CARD also the SO post which seems to be the Gold Standard Gold Standard But it deals with permissions we are not asking about permission. The question deals with finding another way to determine if the SD CARD is mounted. This question only wants to deal with SDK 23+ The article that discuss FUSE is at this link FUSE
We have tried this code that when the emulator has the SD CARD ejected returns or evaluates to TRUE. Other similar configuration from SO have also been tested.
My question is not only how to detect if the SD CARD is mounted but why is this code failing? We are not sure if this code can be tested on an emulator or if a real device is needed. We feel this code failure is because of the concept of the term EXTERNAL storage not meaning an actual SD CARD but making reference to the secondary EXTERNAL storage that is internal.
public boolean chkFORSDCARD() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
System.out.println("#################### IS ####### TRUE "+state);
return true;
}
System.out.println("##################### IS ###### Not Available "+state);
return false;
}
Here is where #james_duh are getting into trouble this line of code as mentioned in your comment `THE_PATH = THE_PATH + "/Documents/"; will not work when the SD CARD is unmounted with this line of code set to [1]
File removable = ContextCompat.getExternalFilesDirs(this, null)[1];
The solution is simple remove the THE_PATH = THE_PATH + "/Documents/";
As for testing if the SD CARD is mounted I am still working on that stay tuned
This code is not real neat but it works. Why you want it to work might be a 64K question ? ? I have tested the code and it works. What might be or concern is the words used to evaluate the path not sure they are or will remain consistent
Here is the code It seems point less to check the state so you can remove that test and construct a new more suitable one I did not get that far
public void onAvail() {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED) || (!state.equals(Environment.MEDIA_MOUNTED_READ_ONLY))) {
File removable = ContextCompat.getExternalFilesDirs(this, null)[1];
THE_PATH = String.valueOf(removable);
if(THE_PATH.contains("Android")){
System.out.println("################################### EXTERNAL Storage "+THE_PATH);
THE_PATH = THE_PATH + "/Documents/";
}else {
File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
String INTERNAL_PATH = String.valueOf(dir);
if(INTERNAL_PATH.contains("emulated")){
System.out.println("######################## $$$$$$$ #### Internal Storage "+INTERNAL_PATH);
}
}
}
}

Samsung Nexus S android 4.0: can't create file in dir on sdcard

I have a Samsung Nexus S device with android 4.0 loaded on it. I am trying to create a file in an existing folder on sdcard and get a "permission denied". In the following code, exists() returns true but canWrite() returns false. Why?
File exst = Environment.getExternalStorageDirectory();
String exstPath = exst.getPath();
File d = new File(exstPath+"/TestDir/");
if (!d.exists())
{
int b = 1;
}
if (!d.canWrite())
{
int a = 1;
}
By the way, I've added <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> to the manifest but that did not help.
Is your phone plugged into your computer? If so, the computer will take control of the SD card and not allow it to be written to. Try changing the connection mode to 'Charge Only' if this is the case.
Append getAbsolutePath() to your first line, then it should work:
File exst = Environment.getExternalStorageDirectory().getAbsolutePath();
Update:
Reviewing my own code and other SO answers, I believe you do not use canWrite to check if a path is writable on a SD card. Instead you use Environment.MEDIA_MOUNTED:
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
Log.d("Test", "sdcard mounted and writable");
}
else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.d("Test", "sdcard mounted readonly");
}
else {
Log.d("Test", "sdcard state: " + state);
}
Although all the answers give partial suggestions, the problem is very likely Samsungs non-standard implementation of the API
String myPath = Environment.getExternalStorageDirectory().getAbsolutePath ;
// or .getName() or .getPath() <-- these don't return everything you need.
// same is true for the file version you're trying.
// you have to append
"/external_sd/"
to the path before the file name.
Here's Samsungs explanation for their "breaking" of the API
http://developer.samsung.com/forum/board/thread/view.do?boardName=GeneralB&messageId=162934&messageNumber=1381&startId=zzzzz~&searchType=TITLE&searchText=sdcard
it's also mentioned in several SO posts, but I don't have the links handy...
H
[Edit Mid May, 2013] Here's the pathology of this problem: you can get the path() by the various normal methods mentioned. Then, just write a simple file and watch it show up in the DDMS file explorer. Try hardcoding that exact path and file name OR use the /external_sd/ thing - in both cases, you will not be able to read your own file back in ! (Or you might, but it will contain garbage.) We've verified this on three different S3 phones. Will test more on Samsungs "real" phones via their RemoteTestingLab site and report back.

Categories

Resources