Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Properly Save To The Pictures Folder

I am trying to save a bitmap that is created by the user to the default 'Pictures' folder on the device. I will start with the option that seems to be the more common method:

public void saveImageToExternalStorage(String filename, Bitmap image) {
    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
    try {
        File directory = new File(path);
        if (!directory.exists()) {
            directory.mkdirs();
        }

        File file = new File(directory, filename + ".jpeg");
        file.createNewFile();

        OutputStream outputStream = new FileOutputStream(file);
        image.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);

        outputStream.flush();
        outputStream.close();
    } catch (IOException exception) {
        exception.printStackTrace();
    }
}

This is supposed to save to the 'Pictures' folder, but Environment.getExternalStorageDirectory().getAbsolutePath() seems to create a new file structure like this file://storage/emulated/11/Pictures/ and save the picture to that. I have also tried Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) with the same result. But this is not the default 'Pictures' folder on any of my test devices or emulators. Which led me to look for other methods, and I found this:

public void saveImageToExternalStorage(String filename, Bitmap image) {
    try {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE, filename);
        values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");

        Uri filepath = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        OutputStream outputStream = getContentResolver().openOutputStream(filepath);

        image.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
        outputStream.close();
    } catch (IOException exception) {
        exception.printStackTrace();
    }
}

This actually saves to the default file structure, which apparently looks like this: content://media/external/images/media/4282. But there seems to be no way to specify the filename of the image (Media.TITLE just sets the title attribute, not the actual filename), it saves as a (seemingly) random string of numbers. I looked at the API Guide for MediaStore.Images.Media and there does not seem to be any other variable that would set the filename. This also does not seem to be the correct way of saving, according to the Android Developer Guides. I would like to know if there is any way of saving to this folder, while also setting my own filename. And if this method could produce unforeseen problems on other devices.

EDIT: For anyone interested this is my current code, based on the answer by @CommonsWare:

public void saveImageToExternalStorage(Context context, String filename, Bitmap image) throws IOException {
    File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    File file = new File(directory, filename + ".jpeg");

    FileOutputStream outputStream = new FileOutputStream(file);
    mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);

    outputStream.flush();
    outputStream.getFD().sync();
    outputStream.close();

    MediaScannerConnection.scanFile(context, new String[] {file.getAbsolutePath()}, null, null);
}
like image 679
Bryan Avatar asked Oct 19 '22 00:10

Bryan


1 Answers

This is supposed to save to the 'Pictures' folder

getExternalStorageDirectory() returns the root of external storage, not some location inside of it (e.g., Pictures/, DCIM/, Movies/).

I have also tried Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) with the same result

That will give you the proper directory, or whatever the device thinks the proper directory is (usually named Pictures/ in external storage for whoever the current user is).

but Environment.getExternalStorageDirectory().getAbsolutePath() seems to create a new file structure like this file://storage/emulated/11/Pictures/

The 11 is a bit unusual, and getAbsolutePath() does not return something with a file:// scheme, but otherwise that seems about right.

But this is not the default 'Pictures' folder on any of my test devices or emulators.

I do not know how you have determined this.

I would like to know if there is any way of saving to this folder, while also setting my own filename.

Start with your first sample, switching to DIRECTORY_PICTURES. Then:

  • have outputStream be a FileOutputStream

  • call outputStream.getFD().sync() after flush() and before close()

  • use MediaScannerConnection and its scanFile() method to arrange for the MediaStore to index the image, so it is available to on-device galleries, desktop OS file managers, etc.

like image 159
CommonsWare Avatar answered Oct 21 '22 22:10

CommonsWare