I want to save a text file to the SD card I inserted into my HTC One M8 running lollipop. However when I run this code it saves to internal storage instead.
String FILENAME = "mysavefile.txt";
File file = new File(Environment.getExternalStorageDirectory(), FILENAME);
if (isExternalStorageWritable()) {
errorSD.setVisibility(View.INVISIBLE);
try {
FileOutputStream fos = new FileOutputStream(file, false);
fos.write(allInformation.getBytes(), 0, 81);
fos.close();
successfulSubmissionToast();
} catch (FileNotFoundException e) {
e.printStackTrace();
errorSD.setVisibility(View.VISIBLE);
} catch (IOException e) {
e.printStackTrace();
}
It should be saving to
/storage/ext_sd
but instead it is saving to
/storage/emulated/0
Then I tried manually entering in the location of my SD card to see if that would work but it ended up throwing the FileNotFoundException
File file = new File("/storage/ext_sd", FILENAME);
Edit:
I believe the issue is that there are multiple external storages. One being permanent and one temporary. The question is how do you access the second one.
First of all, we need to understand about what is difference between Internal Storage, External Storage (aka primary external storage) and Secondary External Storage?
Internal Storage: is storage that is not accessible by the user, except via installed apps (or by rooting their device). Example: data/data/app_packageName
Primary External Storage: In built shared storage which is "accessible by the user by plugging in a USB cable and mounting it as a drive on a host computer". Example: When we say Nexus 5 32 GB.
Secondary External Storage: Removable storage. Example: SD Card.
getExternalStorageDirectory ()
It will return path of the primary external storage directory
To access removable SD (Secondary external storage) there are below APIs:
getExternalFilesDirs(), getExternalCacheDirs(), and getExternalMediaDirs()
Check there documentation for further information
If the Android device following the guide here when deal with multiple external storage, we can use the following code to detect where to write the data for Android kitkat or higher version.
final String APP_EXTERNAL_CACHE = "Android" + File.separator + "data"
+ File.separator + getPackageName() + File.separator + "cache";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
for (File file : getExternalCacheDirs()) {
if (file != null) {
String mountPoint = file.getAbsolutePath().replace(APP_EXTERNAL_CACHE, "");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Environment.isExternalStorageRemovable(file)) {
Log.d(TAG, "removable external " + file.getAbsolutePath());
}
if (Environment.isExternalStorageEmulated(file)) {
Log.d(TAG, "emulated internal " + file.getAbsolutePath());
}
} else {
if (mountPoint.contains("emulated")) {
Log.d(TAG, "emulated internal " + mountPoint);
} else {
Log.d(TAG, "removable external " + mountPoint);
}
}
}
}
}
And declare the relating permission in the manifest.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
I want to save a text file to the SD card I inserted into my HTC One M8 running lollipop
You are welcome to try getExternalFilesDirs(), getExternalCacheDirs(), and getExternalMediaDirs() on Context. Note the plural form of the method name. For these methods, if they return more than one entry, the second and subsequent ones should be on removable media, pointing to directories that you can read and write, without any particular permission (e.g., WRITE_EXTERNAL_STORAGE).
Outside of those locations, for devices that shipped with Android 4.4+, you have no access to removable storage.
I think it's because HTC one modified the sdcard mount point, you should try adb shell ls -l commands to find out which path the sdcard mounted.
For example in nexus 4:
lrwxrwxrwx root root 2015-03-24 18:26 sdcard -> /storage/emulated/legacy
drwxr-x--x root sdcard_r 2015-03-24 18:26 storage
and you should make sure it is not a link: ls -l /storage/emulated/legacy
lrwxrwxrwx root root 2015-03-24 18:26 legacy -> /mnt/shell/emulated/0
The path /mnt/shell/emulated/0 is actually sdcard path.
Try read How can I get external SD card path for Android 4.0+? too, this may help you a lot.
UPDATE
I tried this code:
String test = "/storage/emulated/legacy"; // work
//String test = "/mnt/shell/emulated/legacy"; // not work
File sdDir = new File(test);
if (sdDir.exists()) {
File file = new File(test, "test.sdcard");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
So you'd better try /storage/emulated/* directories to find out which is exactly your sdcard.
Related
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.
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()
I'm trying to write a file to my phone.
I used Environment.getDataDirectory() to know the internal storage's path and Environment.getExternamStorageDirectory() to know the external storage's path.
But when I use Environment.getExternalStorageDirectory() as path, the file is created in internal storage. And when I use Environment.GetDataStorage() as the path, the file is not created. (I am not sure, but I can't find it in the explorer app, at least.)
I think my phone's internal storage is perceived as external storage.(In my case, it has 32 GB amount of storage)
I want to know removable storage(e.g. micro SD card) path. What should I do?
From the official documentation for getExternalStorageDirectory()
Don't be confused by the word "external" here. This directory
can better be thought as media/shared storage. It is a filesystem that
can hold a relatively large amount of data and that is shared across
all applications (does not enforce permissions). Traditionally this is
an SD card, but it may also be implemented as built-in storage in a
device that is distinct from the protected internal storage and can be
mounted as a filesystem on a computer.
So, it can be different from built-in storage in a device.
For your case, you could use getExternalStoragePublicDirectory(java.lang.String)
This is where the user will typically place and manage their own
files
The path here should be one of DIRECTORY_MUSIC, DIRECTORY_PODCASTS,
DIRECTORY_RINGTONES, DIRECTORY_ALARMS, DIRECTORY_NOTIFICATIONS,
DIRECTORY_PICTURES, DIRECTORY_MOVIES, DIRECTORY_DOWNLOADS, or
DIRECTORY_DCIM. May not be null.
Or if you want your data to be deleted whenever the user uninstalls your app, you could use getExternalFilesDir().
As these files are internal to the applications, and not typically visible to the user as media.
Also there are some differences between getFilesDir() and getExternalFilesDir()
External files are not always available: they will disappear if the user mounts the external storage on a computer or removes it. See the APIs on environment for information in the storage state.
There is no security enforced with these files. For example, any application holding WRITE_EXTERNAL_STORAGE can write to these files.
Try this...
static String storagestate = Environment.getExternalStorageState();
private static FileOutputStream outStream;
private static File imageFilepath;
public static String saveImage(Bitmap bitmap) {
File folder = null;
// Check for SD card
if (storagestate.equals(Environment.MEDIA_MOUNTED)) {
folder = new File(Environment.getExternalStorageDirectory(),
"*YourStorageNameInDevice");
if (!folder.exists()) {
folder.mkdir();
}
outStream = null;
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
.format(new Date());
// Getting filepath
imageFilepath = new File(folder.getPath() + File.separator
+ timestamp + ".PNG");
try {
outStream = new FileOutputStream(imageFilepath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return imageFilepath.getAbsolutePath();
}
}
I have an application that performs a write operation on an external SD Card (external as in not the "external" in the device's flash memory), but as it has been thoroughly discussed here and in quite a few other questions, it appears there is no generic way / supported API to retrieve an SD Card mounting point that works on every phone, from different manufacturers.
With that in mind, something like the code below needs to be done to find out if an external storage device is mounted and where:
final String state = Environment.getExternalStorageState();
if ( Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) { // we can read the External Storage...
//Retrieve the primary External Storage:
final File primaryExternalStorage = Environment.getExternalStorageDirectory();
//Retrieve the External Storages' root directory:
final String externalStorageRootDir;
if ( (externalStorageRootDir = primaryExternalStorage.getParent()) == null ) { // no parent...
Log.i("SD Card Path", "External Storage: " + primaryExternalStorage + "\n");
}
else {
final File externalStorageRoot = new File( externalStorageRootDir );
final File[] files = externalStorageRoot.listFiles();
for ( final File file : files ) {
if ( file.isDirectory() && file.canRead() && (file.listFiles().length > 0) ) { // it is a real directory (not a USB drive)...
Log.i("SD Card Path", "External Storage: " + file.getAbsolutePath() + "\n");
}
}
}
}
I could be wrong, but I understand the SD Card could be completely empty and that last condition (file.listFiles().length > 0) could return false. So I thought, instead of checking that length, I would add file.canWrite(). That worked as I expected on the phone I am currently testing on (Motorola Atrix MB860 - it's old, I know, but one of my requirements is make it compatible with versions starting from Gingerbread, API 9), but I am unsure whether or not that condition could return true to /mnt/asec/ or /mnt/obb on other phones , recent or not, since their both readable directories, thus also satisfying the first two conditions (and could even return true to (file.listFiles().length > 0) if it had something written on it (like a 50MB+ size file on /mnt/obb).
So, in short, what I need to know is whether an app will ever have write permission to these two locations or if only the system has such permission. If yes, what else can I check for in order to exclude them from the final path I wish to obtain: the external SD Card installed on the device?
Thank you in advance for any help!
This is the output on a Nexus 5
shell#hammerhead:/ $ ls -la /mnt/
drwxr-xr-x root system 1970-09-18 01:42 asec
drwx------ media_rw media_rw 1970-09-18 01:42 media_rw
drwxr-xr-x root system 1970-09-18 01:42 obb
as you can see only root has write permission on the directory. So the only way an app can write that directory is to be executed as root
I have an application where I test all storages whether they are accessible. As you probably know, Google starts to limit access to external storages. So Kitkat devices can have read-only storages that have writable access only to application specific directory (/Android/data/...).
I test paths of all storages whether they are writable by testing:
if(new File("/storage_root_path/").canWrite() && new File("/storage_root_path/AppDir/").canWrite())
{
//storage is writable
}
else
{
//storage is readonly
}
/AppDir/ is directory of my app on root of storage. These tests work well for most devices. But I have one user with Samsung Galaxy S4 SGH-M919 (Kitkat) and both tests return true on his external sd card. But the storage is not writable.
Is there any other way how to determine that storage is read-only on Kitkat ? I can try to create folder but I would like to have some better and faster solution.
Thank you for any advice!
Confirm. One should really check file for writability, File.canWrite() doesn't help. I'm using this kind of function:
private boolean isFileWritable(File file) {
try {
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel fc=raf.getChannel();
FileLock fl=fc.lock();
fl.release();
fc.close();
raf.close();
return true;
}
catch(Exception ex) {
return false;
}
}