Reputation: 3572
When my app first starts, it lets the user select a profile picture. This can be done taking a photo at the moment, or selecting it from the gallery.
After the user gets a picture, this must be saved in the device's internal storage and will be used in the app as user's profile picture.
This process works fine, the user gets the picture and this is shown in an imageview before it's saved. But to save the image in the internal storage, I'm having some trouble. I have tried several ways to do it, and most of them seem to work. But when I try them, the picture is not being saved or at least I don't find the folder where it is being saved.
I have tried in these 3 ways:
First:
File directory = getDir("profile", Context.MODE_PRIVATE);
File mypath = new File(directory, "thumbnail.png");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
mybitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
Second:
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
mybitmap.compress(Bitmap.CompressFormat.PNG, 90, bytes);
FileOutputStream fos = null;
try {
fos = openFileOutput("thumbnail.png", Context.MODE_PRIVATE);
fos.write(bytes.toByteArray());
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Third:
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
mybitmap.compress(Bitmap.CompressFormat.PNG, 90, bytes);
File fileWithinMyDir = new File(getFilesDir(), "thumbnail.png");
try {
FileOutputStream fos = new FileOutputStream(fileWithinMyDir);
fos.write(bytes.toByteArray());
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Suposedly, the image gets saved in the path: android/data/AppName/app_data/
but there is no folder created there. Anyway I have looked in other folders, but nothing.
EDIT-
In the first method, I've seen that is throwing an exception:
E/SAVE_IMAGE﹕ /data/data/com.example.myapp/app_profile/thumbnail.png: open failed: EISDIR (Is a directory)
java.io.FileNotFoundException: /data/data/com.example.myapp/app_profile/thumbnail.png: open failed: EISDIR (Is a directory)
Caused by: libcore.io.ErrnoException: open failed: EISDIR (Is a directory)
Upvotes: 5
Views: 22189
Reputation: 199
You can use this ImageSaver
class for save bitmap image to your app folder.
The image saver class code is given below:
public class ImageSaver {
private String directoryName = "images";
private String fileName = "image.png";
private Context context;
private File dir;
private boolean external=false;
public ImageSaver(Context context) {
this.context = context;
}
public ImageSaver setFileName(String fileName) {
this.fileName = fileName;
return this;
}
public ImageSaver setExternal(boolean external) {
this.external = external;
return this;
}
public ImageSaver setDirectory(String directoryName) {
this.directoryName = directoryName;
return this;
}
public void save(Bitmap bitmapImage) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(createFile());
bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@NonNull
private File createFile() {
File directory;
if (external) {
directory = getAlbumStorageDir(directoryName);
if (!directory.exists()){
directory.mkdir();
}
} else {
directory = new File(context.getFilesDir()+"/"+directoryName);
if (!directory.exists()){
directory.mkdir();
}
}
return new File(directory, fileName);
}
private File getAlbumStorageDir(String albumName) {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e("ImageSaver", "Directory not created");
}
return file;
}
public static boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
public static boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
}
public Bitmap load() {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(createFile());
return BitmapFactory.decodeStream(inputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public boolean deleteFile() {
File file = createFile();
return file.delete();
}
}
Then, in your activity where you are getting the bitmap from the server—by using Glide or Picasso, but you can use any method—you should set setExternal
before you call .setDirectory
:
Bitmap bitmap=........//bitmap from code
new ImageSaver(this)
.setFileName("filename.jpg")
.setExternal(false)//image save in external directory or app folder default value is false
.setDirectory("dir_name")
.save(bitmap); //Bitmap from your code
Upvotes: 4
Reputation: 987
First Thanks @masmic. I use it to make some search in github repos and made my own way. Note that I use kotlin, coroutines and crashlitics you can get rid of all that with Java, posting a thread to a Handler and send data to your custom sever with okhttp.
My code can Save bitmap to app folder i.e. local store. and read load it is:.
package xxxx.data
import android.content.Context
import android.content.ContextWrapper
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.util.Date
interface BitmapDAO {
suspend fun insertBitmapAndGetRelativePath(bitmapEntity: PhotoEntity): String?
suspend fun getBitmapFromPath(bitmapEntity: PhotoEntity): Bitmap?
suspend fun deleteFile(bitmapEntity: PhotoEntity) : Boolean
suspend fun updateBitmap(bitmapEntity: PhotoEntity): Boolean
}
class BitmapDAOImpl(private val mContext: Context): BitmapDAO {
companion object{
//private const val MIME_TYPE_IMAGE:String = "image/*"
private const val FILE_EXTENSION:String = ".jpeg"
private const val FOLDER:String = "images"
}
private fun convertFromBitMapToByteArray(bitmap: Bitmap) : ByteArray {
val byteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG,100,byteArrayOutputStream)
return byteArrayOutputStream.toByteArray()
}
override suspend fun insertBitmapAndGetRelativePath(bitmapEntity: PhotoEntity): String? {
val bitmap: Bitmap = bitmapEntity.bitmap.let { bitmap ->
return@let bitmap
}?: return null
val productName: String = bitmapEntity.productName.let {
productName -> return@let productName
}?: return null
// Get the context wrapper
val wrapper = ContextWrapper(mContext)
val fileName: String = productName + Date().time.toString() + FILE_EXTENSION
try {
/**
* Caution: On devices that run Android 7.0 (API level 24) or higher,
* unless you pass the Context.MODE_PRIVATE file mode
* into openFileOutput(), a SecurityException occurs.
*/
return withContext(Dispatchers.IO) {
// Initialize a new file instance to save bitmap object
var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
if (!fileDir.exists()) {
fileDir.mkdir()
}
fileDir = File(fileDir, fileName)
//with mContext.openFileOutput It gets: EISDIR (Is a directory)
FileOutputStream(fileDir).use {
it.write(convertFromBitMapToByteArray(bitmap))
}
//Uri.parse(file.absolutePath)
return@withContext fileName
}
} catch (ex: FileNotFoundException) {
//REPORT
Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
Firebase.crashlytics.setCustomKey("Method", "insertBitmapAndGetUri")
Firebase.crashlytics.setCustomKey("UseCase", "saveImage")
Firebase.crashlytics.recordException(ex)
Log.e("BitmapDAOImpl FileNotFoundException", ex.message ?: "")
Log.e("BitmapDAOImpl FileNotFoundException", ex.stackTrace.toString())
} catch (ex: IOException) {
//REPORT
Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
Firebase.crashlytics.setCustomKey("Method", "insertBitmapAndGetUri")
Firebase.crashlytics.setCustomKey("UseCase", "saveImage")
Firebase.crashlytics.recordException(ex)
Log.e("BitmapDAOImpl IOException", ex.message ?: "")
Log.e("BitmapDAOImpl IOException", ex.stackTrace.toString())
}
//only for Exception case
return null
}
override suspend fun getBitmapFromPath(bitmapEntity: PhotoEntity): Bitmap? {
try {
val wrapper = ContextWrapper(mContext)
return withContext(Dispatchers.IO) {
var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
if (!fileDir.exists()) {
fileDir.mkdir()
}
fileDir = File(fileDir, bitmapEntity.fileName)
var payload : Bitmap? = null
if (fileDir.exists()){
FileInputStream(fileDir).use { fileInputStream ->
/**
* Loading image
*/
payload = BitmapFactory.decodeStream(fileInputStream)
}
}
return@withContext payload
}
} catch (ex: FileNotFoundException) {
//REPORT
Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
Firebase.crashlytics.setCustomKey("Method", "getBitmapFromUri")
Firebase.crashlytics.setCustomKey("UseCase", "readImage")
Firebase.crashlytics.recordException(ex)
Log.e("BitmapDAOImpl", ex.message ?: "")
Log.e("BitmapDAOImpl", ex.stackTrace.toString())
} catch (out: OutOfMemoryError) {
//REPORT
Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
Firebase.crashlytics.setCustomKey("Method", "getBitmapFromUri")
Firebase.crashlytics.setCustomKey("UseCase", "readImage")
Firebase.crashlytics.recordException(out)
Log.e("BitmapDAOImpl", out.message ?: "")
Log.e("BitmapDAOImpl", out.stackTrace.toString())
}
// If an error has occurred or the album ID is null, then return a default artwork image
//return BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_launcher_foreground)
//only for Exception case
return null
}
override suspend fun deleteFile(bitmapEntity: PhotoEntity) : Boolean {
val wrapper = ContextWrapper(mContext)
try {
return withContext(Dispatchers.IO) {
var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
if (!fileDir.exists()) {
fileDir.mkdir()
}
fileDir = File(fileDir, bitmapEntity.fileName)
if(fileDir.exists()){
return@withContext fileDir.delete()
} else {
return@withContext false
}
}
} catch (ex: FileNotFoundException) {
//REPORT
Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
Firebase.crashlytics.setCustomKey("Method", "deleteFile")
Firebase.crashlytics.setCustomKey("UseCase", "deleteImage")
Firebase.crashlytics.recordException(ex)
Log.e("BitmapDAOImpl", ex.message ?: "")
Log.e("BitmapDAOImpl", ex.stackTrace.toString())
}
//only for Exception case
return false
}
override suspend fun updateBitmap(bitmapEntity: PhotoEntity):Boolean {
val newImage: Bitmap = bitmapEntity.bitmap.let { newImage ->
return@let newImage
}?: return false
val wrapper = ContextWrapper(mContext)
try {
return withContext(Dispatchers.IO) {
var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
if (!fileDir.exists()) {
fileDir.mkdir()
}
fileDir = File(fileDir, bitmapEntity.fileName)
//if(fileDir.exists()) ?? for both case is the same
FileOutputStream(fileDir).use {
it.write(convertFromBitMapToByteArray(newImage))
}
return@withContext true
}
} catch (ex: Exception) {
//REPORT
Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
Firebase.crashlytics.setCustomKey("Method", "updateBitmapAndGetUri")
Firebase.crashlytics.setCustomKey("UseCase", "updateImage")
Firebase.crashlytics.recordException(ex)
Log.e("BitmapDAOImpl", ex.message ?: "")
Log.e("BitmapDAOImpl", ex.stackTrace.toString())
}
return false
}
} //End of class
Thanks. And have a nice day.
Upvotes: 0
Reputation: 3572
After trying several things, this is what finally has worked for me:
ContextWrapper cw = new ContextWrapper(getApplicationContext());
File directory = cw.getDir("profile", Context.MODE_PRIVATE);
if (!directory.exists()) {
directory.mkdir();
}
File mypath = new File(directory, "thumbnail.png");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
resizedbitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
} catch (Exception e) {
Log.e("SAVE_IMAGE", e.getMessage(), e);
}
Basically the thing is to check if the directory (not the file) exists, and if not, create it with mkdir().
Upvotes: 9
Reputation: 39
understand your requirement. Below pasting some piece of code which i tested and working.Basically get the image from camera and save it in app storage.please go through it. Hope this helps.Cheers..
//For saving image...
private String saveToInternalSorage(Bitmap bitmapImage) {
ContextWrapper cw = new ContextWrapper(getApplicationContext());
File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
// Create imageDir
File mypath = new File(directory, "profile.jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
// Use the compress method on the BitMap object to write image to
// the OutputStream
bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
Editor editor = sharedpreferences.edit();
editor.putString("saved", "na");
editor.commit();
} catch (Exception e) {
e.printStackTrace();
}
return directory.getAbsolutePath();
}
//..To load image from storage
private void loadImageFromStorage(String path) {
try {
ContextWrapper cw = new ContextWrapper(getApplicationContext());
File path1 = cw.getDir("imageDir", Context.MODE_PRIVATE);
File f = new File(path1, "profile.jpg");
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));
ImageView img = (ImageView) findViewById(R.id.viewImage);
img.setImageBitmap(b);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Upvotes: 1
Reputation: 11477
Try this ...
You should use the Bitmap.compress() method to save a Bitmap as a file. It will compress (if the format used allows it) your picture and push it into an OutputStream.
Here is an example of a Bitmap instance obtained through getImageBitmap(myurl) that can be compressed as a JPEG with a compression rate of 85% :
String path = Environment.getExternalStorageDirectory().toString();
OutputStream fOut = null;
File file = new File(path, "FitnessGirl"+Contador+".jpg"); // the File to save to
fOut = new FileOutputStream(file);
Bitmap pictureBitmap = getImageBitmap(myurl); // obtaining the Bitmap
pictureBitmap.compress(Bitmap.CompressFormat.JPEG, 85, fOut); // saving the Bitmap to a file compressed as a JPEG with 85% compression rate
fOut.flush();
fOut.close(); // do not forget to close the stream
MediaStore.Images.Media.insertImage(getContentResolver(),file.getAbsolutePath(),file.getName(),file.getName());
Upvotes: 0
Reputation: 3439
Well, you need to create the folder if it doesn't exist.
try running this code and see if it fixes your issue:
File parentDestination = saveFile.getParentFile();
if (!parentDestination.exists()) {
parentDestination.mkdirs(); //make all the directory structures needed
}
Upvotes: 1