Android supports all the standard Java file I/O APIs for create, read, update, and delete (CRUD) operations, along with some additional helpers to make accessing those files in specific locations a little more convenient. There are three main locations in which an application can work with files:
WRITE_EXTERNAL_STORAGE
permission in API Level 4+. Often, this is a physical SD card in the device.However, there are several complications.
First, some devices don’t have removable storage. On these, the external storage directory always exists—it is just in a different partition of the same flash memory storage as internal storage.
Second, on devices that do have removable storage, the storage might be removed at the time your application checks it. There’s no point trying to write it if it’s not there.
Read from res directory
If your application requires external file resources, you can include them in your distribution package by placing them in the res/raw folder of your project hierarchy.
To access these read-only file resources, call the openRawResource
method from your application’s Resource object to receive an InputStream based on the specified file. Pass in the filename (without the extension) as the variable name from the R.raw class, as shown in the following code
Resources r = getResources(); InputStream file = r.openRawResource(R.raw.filename);
Read from assets directory
Android offers one more directory where you can keep files which also will be included in package. This directory called /assets. There are some difference from res directory.
With resources, there's built-in support for providing alternatives for different languages, OS versions, screen orientations, etc., as described here. None of that is available with assets. Also, many parts of the API support the use of resource identifiers. Finally, the names of the resources are turned into constant field names that are checked at compile time, so there's less of an opportunity for mismatches between the code and the resources themselves. None of that applies to assets.source
Context context = getApplicationContext(); InputStream is = context.getAssets().open(filename); String text = ""; int size = is.available(); byte[] buffer = new byte[size]; is.read(buffer); is.close(); text = new String(buffer);
Read/Write to Internal Storage
This area of storage is sort of private to the application. It is always available to the application and gets purged when the app is uninstalled by the user.
Internal storage refers to the hard drive on device. Internal storage gives you the ability to prevent other applications from accessing the files you save and are tied directly to your app.
Files stored in /data/data/packagename/files/filename.txt. There are few modes for file access
Internal storage can be accessed using the Context
methods openFileInput(String filename)
, which returns a FileInputStream
object, or openFileOutput(String filename, int mode)
, which returns a FileOutputStream
.
Write to file in internal storage
String FILE_NAME = "file.txt"; try { FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); fos.write(someText.toString().getBytes()); fos.close(); } catch (Exception e) { e.printStackTrace(); }
Read from file in internal storage
try { BufferedReader bReader = new BufferedReader(new InputStreamReader(openFileInput(FILE_NAME))); String line; StringBuffer text = new StringBuffer(); while ((line = bReader.readLine()) != null) { text.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); }
The Context
method getFilesDir()
returns the root of this directory, and you can then access it using normal java.io
methods and classes.
// Get the absolute path to the directory for our app's internal storage File where = getFilesDir(); Log.d(TAG, "Our private dir is " + where.getAbsolutePath());
Data is written to the file streams as bytes, so higher-level data (even strings) must be converted into and out of this format.
Following is a method to save an image to internal storage in Kotlin
private fun saveImageToInternalStorage(drawableId:Int):Uri{ // Get the image from drawable resource as drawable object val drawable = ContextCompat.getDrawable(applicationContext,drawableId) // Get the bitmap from drawable object val bitmap = (drawable as BitmapDrawable).bitmap // Get the context wrapper instance val wrapper = ContextWrapper(applicationContext) // Initializing a new file // The bellow line return a directory in internal storage var file = wrapper.getDir("images", Context.MODE_PRIVATE) // Create a file to save the image file = File(file, "${UUID.randomUUID()}.jpg") try { // Get the file output stream val stream: OutputStream = FileOutputStream(file) // Compress bitmap bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream) // Flush the stream stream.flush() // Close stream stream.close() } catch (e: IOException){ // Catch the exception e.printStackTrace() } // Return the saved image uri return Uri.parse(file.absolutePath) }
Read/Write to SDCard
The key differentiator between internal and external storage is that external storage is mountable. This means that the user can connect his or her device to a computer and have the option of mounting that external storage as a removable disk on the PC. Often, the storage itself is physically removable (such as an SD card), but this is not a requirement of the platform.
External storage is typically either a removable storage media (i.e. SD Card) or an internal non-removable storage that is accessed in the same manner.
The most important thing to remember when storing files on external storage is that no security is enforced on files stored here. Any application can access, overwrite, or delete files stored on the external storage.
In order to write data to SDCard, the application need permission WRITE_EXTERNAL_STORAGE, which can be specified in the file AndroidManifest.xml.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Check external storage
private static boolean isExternalStorageReadOnly() { String extStorageState = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)) { return true; } return false; } private static boolean isExternalStorageAvailable() { String extStorageState = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(extStorageState)) { return true; } return false; }
Write to SDCard
String FILE_NAME = "file.txt"; if (isExternalStorageAvailable() && isExternalStorageReadOnly()) { String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath(); File file = new File(baseDir, FILE_NAME); FileWriter writer = null; try { writer = new FileWriter(file); writer.write(text.toString()); writer.close(); } catch (IOException e) { e.printStackTrace(); } }
Read from SDCard
String FILE_NAME = "file.txt"; if (isExternalStorageAvailable() && isExternalStorageReadOnly()) { String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath(); File file = new File(baseDir, FILE_NAME); String line = ""; StringBuilder text = new StringBuilder(); try { FileReader fReader = new FileReader(file); BufferedReader bReader = new BufferedReader(fReader); while( (line = bReader.readLine()) != null ){ text.append(line+"\n"); } } catch (IOException e) { e.printStackTrace(); } }
Following is a method to save an image to external storage in Kotlin
private fun saveImageToExternalStorage(bitmap:Bitmap):Uri{ // Get the external storage directory path val path = Environment.getExternalStorageDirectory().toString() // Create a file to save the image val file = File(path, "${UUID.randomUUID()}.jpg") try { // Get the file output stream val stream: OutputStream = FileOutputStream(file) // Compress the bitmap bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream) // Flush the output stream stream.flush() // Close the output stream stream.close() toast("Image saved successful.") } catch (e: IOException){ // Catch the exception e.printStackTrace() toast("Error to save image.") } // Return the saved image path to uri return Uri.parse(file.absolutePath) }
Read/Write to Cache
Should your application need to cache temporary files, Android offers both a managed internal cache, and (since Android API level 8) an unmanaged external cache. You can access them by calling the getCacheDir and getExternalCacheDir methods, respectively, from the current Context.
Files stored in either cache location will be erased when the application is uninstalled. Files stored in the internal cache will potentially be erased by the system when it is running low on available storage; files stored on the external cache will not be erased, as the system does not track available storage on external media.
Read from cache dir
String TMP_FILE_NAME = "tmp_file.txt"; File tmpFile; File cacheDir = getBaseContext().getCacheDir(); tmpFile = new File(cacheDir.getPath() + "/" + TMP_FILE_NAME) ; String line=""; StringBuilder text = new StringBuilder(); try { FileReader fReader = new FileReader(tmpFile); BufferedReader bReader = new BufferedReader(fReader); while( (line=bReader.readLine()) != null ){ text.append(line+"\n"); } } catch (FileNotFoundException e) { e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }
Write to cache dir
FileWriter writer = null; try { writer = new FileWriter(tmpFile); writer.write(text.toString()); writer.close(); // path to file // tmpFile.getPath() } catch (IOException e) { e.printStackTrace(); }
Read/Write to publicly readable files
Android 2.2 (API level 8) includes a convenience method, Environment.getExternalStoragePublicDirectory
, that can be used to find a path in which to store your application files. The returned location is where users will typically place and manage their own files of each type. Files that’ll remain on the storage even after the application is uninstalled by the user like media (photos, videos, etc.) or other downloaded files. This is particularly useful for applications that provide functionality that replaces or augments system applications, such as the camera, that store files in standard locations.
There are 2 methods that we can use to get the public external storage directory for placing files:
getExternalStorageDirectory()
method returns the primary (top-level or root) external storage directory.getExternalStoragePublicDirectorty
method returns a top level public external storage directory for showing files of a particular type based on the argument passed. So basically the external storage has directories like Music, Podcasts, Pictures, etc. whose paths can be determined and returned via this function by passing the appropriate environment constants.The getExternalStoragePublicDirectory
method accepts a string parameter that determines which subdirectory you want to access using a series of Environment static constants:
Note that if the returned directory doesn’t exit, you must create it before writing fi les to the directory, as shown in the following snippet
String IMAGE_FILE_NAME = "image.png"; File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File file = new File(path, FILE_NAME); try { path.mkdirs(); // statements for file store } catch (IOException e) { Log.d(TAG, "Error writing " + IMAGE_FILE_NAME, e); }
External System Directories
There are additional methods in Environment
and Context
that provide standard locations on external storage where specific files can be written. Some of these locations have additional properties as well.
Environment.getExternalStoragePublicDirectory(String type)
.
MediaStore
for applications such as the Gallery.DIRECTORY_PICTURES
, DIRECTORY_MUSIC
, DIRECTORY_MOVIES
, and DIRECTORY_RINGTONES
.Context.getExternalFilesDir(String type)
.
MediaStore
.DIRECTORY_PICTURES
, DIRECTORY_MUSIC
, DIRECTORY_MOVIES
, and DIRECTORY_RINGTONES
.Context.getExternalCacheDir()
.
Context.getExternalFilesDirs()
and Context.getExternalCacheDirs()
.
Context.getExternalMediaDirs()
.
Sharing files via a FileProvider
Sometimes you want to share internal-storage files with another app, without the bother of putting the data into a Cursor
and creating a ContentProvider.
The FileProvider
class allows you to make files available to another application, usually in response to an Intent. It is simpler to set up than a ContentProvider
, but is actually a subclass of ContentProvider
.
This example exposes a photo.jpg file from one application to another. For this example I have created an Android Studio project called FileProviderDemo
, which contains two different applications in two different packages, providerapp
and requestingapp
. We’ll start by discussing the Provider
app since it contains the actual FileProvider
. However, you have to run the Requester
application first, as it will start the Provider
app.
Unlike the ContentProvider
case, you rarely have to write code for the provider itself; instead, use the FileProvider
class directly as a provider in your AndroidManifest.xml file, as shown in following example.
The provider definition
<provider android:name="android.support.v4.content.FileProvider" android:authorities="me.proft.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>
The provider does not have to be exported for this usage, but must have the ability to grant Uri
permissions as shown. The meta-data
element gives the name of a simple mapping file, which is required to map "virtual paths" to actual paths, as shown.
The filepaths.xml file
<paths> <files-path path="secrets/" name="shared_secrets"/> </paths>
Finally, there has to be an Activity
to provide the Uri
to the requested file. In our example this is the ProvidingActivity
, shown in following example.
public class ProvidingActivity extends AppCompatActivity { private File requestFile; private Intent resultIntent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); resultIntent = new Intent("me.proft.fileprovider.ACTION_RETURN_FILE"); setContentView(R.layout.activity_providing); // The Layout provides a text field with text like // "If you agree to provide the file, press the Agree button" Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { provideFile(); } }); requestFile = new File(getFilesDir(), "secrets/demo.txt"); // On first run of application, create the "hidden" file in internal storage if (!requestFile.exists()) { requestFile.getParentFile().mkdirs(); try (PrintWriter pout = new PrintWriter(mRequestFile)) { pout.println("This is the revealed text"); pout.println("And then some."); } catch (IOException e) { e.printStackTrace(); } } private void provideFile() { // The approved target is one hardcoded file in our directory requestFile = new File(getFilesDir(), "secrets/demo.txt"); Uri fileUri = FileProvider.getUriForFile(this, "me.proft.fileprovider", requestFile); // The requester is in a different app so can't normally read our files! resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); resultIntent.setDataAndType(fileUri, getContentResolver().getType(fileUri)); // Attach that to the result Intent resultIntent.setData(fileUri); // Set the result to be "success" + the result setResult(Activity.RESULT_OK, resultIntent); finish(); } }
The important part of this code is in the provideFile()
method, which:
Uri
for the actual file (in this trivial example there is only one file, with a hardcoded filename)Intent
to let the receiving app read this one file (only) with our permissionsUri
as data to the result Intent
Intent
, and the "success" flags to Activity.RESULT_OK
• Calls finish()
to end the Activity
Remember that the point of the FileProvider
is to share files from one application to another, running with different user permissions. Our second application also has only one Activity
, the "requesting" Activity. Most of this is pretty standard boilerplate code. In onCreate()
, we create the requesting Intent
:
requestFileIntent = new Intent(Intent.ACTION_PICK); requestFileIntent.setType("text/plain");
The main part of the UI is a text area, which initially suggests that you request a file by pressing the button. That button’s action listener is only one line:
startActivityForResult(requestFileIntent, ACTION_GET_FILE);
This will result in a subsequent call to onActivityComplete()
, which is shown in following example.
public class RequestingActivity extends AppCompatActivity { private static final int ACTION_GET_FILE = 1; private Intent requestFileIntent; ... @Override protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) { if (requestCode == ACTION_GET_FILE) { if (resultCode == Activity.RESULT_OK) { try { // get the file Uri returnUri = resultIntent.getData(); final InputStream is = getContentResolver().openInputStream(returnUri); final BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line; TextView fileViewTextArea = (TextView) findViewById(R.id.fileView); fileViewTextArea.setText(""); // reset each time while ((line = br.readLine()) != null) { fileViewTextArea.append(line); fileViewTextArea.append("\n"); } } catch (IOException e) { Toast.makeText(this, "IO Error: " + e, Toast.LENGTH_LONG).show(); } } else { Toast.makeText(this, "Request denied or canceled", Toast.LENGTH_LONG).show(); } return; } // For any other Activity, we can do nothing... super.onActivityResult(requestCode, resultCode, resultIntent); } }
Assuming that the request succeeds, you will get called here with requestCode
set to the only valid action, RESULT_OK
, and the resultIntent
being the one that the providing Activity
set as the Activity
result—that is, the Intent
wrapping the Uri
that we need in order to read the file! So we just get the Uri
from the Intent
and open that as an input stream, and we can read the "secret" file from the providing application’s otherwise - private internal storage.
How to check free and used space in internal and external storage
There is auxiliary class to get information about free and used space in internal and external storage
public class DeviceMemory { public static long getInternalTotalSpaceInMB() { StatFs statFs = new StatFs(Environment.getDataDirectory().getAbsolutePath()); long total = ((long)statFs.getBlockCount() * (long)statFs.getBlockSize()) / 1048576; return total; } public static long getInternalFreeSpaceInMB() { StatFs statFs = new StatFs(Environment.getDataDirectory().getAbsolutePath()); long free = ((long)statFs.getAvailableBlocks() * (long)statFs.getBlockSize()) / 1048576; return free; } public static long getInternalUsedSpaceInMB() { StatFs statFs = new StatFs(Environment.getDataDirectory().getAbsolutePath()); long total = ((long)statFs.getBlockCount() * (long)statFs.getBlockSize()) / 1048576; long free = ((long)statFs.getAvailableBlocks() * (long)statFs.getBlockSize()) / 1048576; long busy = total - free; return busy; } public static boolean isExternalStorage() { if (Environment.isExternalStorageRemovable()) { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_MOUNTED_READ_ONLY); } else { return false; } } public static long getExternalTotalSpaceInMB() { long total = 0; if (isExternalStorage()) { StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath()); total = ((long) statFs.getBlockCount() * (long) statFs.getBlockSize()) / 1048576; } return total; } public static long getExternalFreeSpaceInMB() { long free = 0; if (isExternalStorage()) { StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath()); free = ((long) statFs.getAvailableBlocks() * (long) statFs.getBlockSize()) / 1048576; } return free; } public static long getExternalUsedSpaceInMB() { long busy = 0; if (isExternalStorage()) { StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath()); long total = ((long) statFs.getBlockCount() * (long) statFs.getBlockSize()) / 1048576; long free = ((long) statFs.getAvailableBlocks() * (long) statFs.getBlockSize()) / 1048576; busy = total - free; } return busy; } }
How to download photo from url and save to storage
To download photo from url you can use android-async-http.
final String url = "http://url/image.jpg"; final String subDir = "Photos"; AsyncHttpClient client = new AsyncHttpClient(); client.get(url, new FileAsyncHttpResponseHandler(ctx) { @Override public void onSuccess(int statusCode, Header[] headers, File response) { if (statusCode == 200) { String filename = String.format("%s.%s", System.currentTimeMillis(), MimeTypeMap.getFileExtensionFromUrl(url)); File file = savePhoto(filename, response); iv.setImageURI(android.net.Uri.parse(file.toURI().toString())); } else { Log.d(TAG, "status error " + statusCode); } } @Override public void onFailure(int statusCode, Header[] headers, Throwable e, File response) { Log.d(TAG, "onFailure: " + e.toString()); } }); private File savePhoto(String filename, File file) { File output = null; if (DeviceMemory.isExternalStorage() && DeviceMemory.getExternalFreeSpaceInMB() > 5) { output = saveToExternalStorage(filename, file, subDir); } else if (DeviceMemory.getInternalFreeSpaceInMB() > 5) { output = saveToInternalStorage(filename, file); } else { Toast.makeText(activity, "There is no enough space to save image", Toast.LENGTH_SHORT).show(); } return output; } private File saveToInternalStorage(String filename, File file) { byte[] byteArray = null; try { InputStream inputStream = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] b = new byte[1024*8]; int bytesRead = 0; while ((bytesRead = inputStream.read(b)) != -1) { bos.write(b, 0, bytesRead); } byteArray = bos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } try { FileOutputStream fOut = openFileOutput(filename, MODE_PRIVATE); fOut.write(byteArray); fOut.close(); } catch (IOException e) { e.printStackTrace(); } String dataDir = getFilesDir().getAbsolutePath(); return new File(dataDir, filename); } private File saveToExternalStorage(String filename, File file, String subDir) { byte[] byteArray = null; try { InputStream inputStream = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] b = new byte[1024*8]; int bytesRead = 0; while ((bytesRead = inputStream.read(b)) != -1) { bos.write(b, 0, bytesRead); } byteArray = bos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } File dirOut = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), subDir); if (!dirOut.exists()) { dirOut.mkdirs(); } File fileOut = new File(dirOut.getAbsolutePath(), filename); try { FileOutputStream fOut = new FileOutputStream(fileOut); fOut.write(byteArray); fOut.close(); } catch (IOException e) { e.printStackTrace(); } return fileOut; }
How to get file extension and mime type
You can get file extension and mime type:
File file = ... Uri uri = Uri.fromFile(file); String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString()); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
Method to save an image to gallery and return URI in Kotlin
private fun saveImage(drawable:Int, title:String):Uri{ // Get the image from drawable resource as drawable object val drawable = ContextCompat.getDrawable(applicationContext,drawable) // Get the bitmap from drawable object val bitmap = (drawable as BitmapDrawable).bitmap // Save image to gallery val savedImageURL = MediaStore.Images.Media.insertImage( contentResolver, bitmap, title, "Image of $title" ) // Parse the gallery image url to uri return Uri.parse(savedImageURL) }