如何正确保存到图片文件夹

How To Properly Save To The Pictures Folder

我正在尝试将用户创建的位图保存到设备上的默认 'Pictures' 文件夹中。我会从似乎更常见的方法的选项开始:

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();
    }
}

这应该保存到 'Pictures' 文件夹,但 Environment.getExternalStorageDirectory().getAbsolutePath() 似乎创建了一个像这样的新文件结构 file://storage/emulated/11/Pictures/ 并将图片保存到该文件夹​​中。我也试过 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 结果相同。但这不是我的任何测试设备或模拟器上的默认 'Pictures' 文件夹。这促使我寻找其他方法,我发现了这个:

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();
    }
}

这实际上是保存到默认文件结构,看起来像这样:content://media/external/images/media/4282。但是似乎没有办法指定图像的文件名(Media.TITLE只是设置了标题属性,而不是实际的文件名),它保存为(看似)随机的数字字符串。我看了看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。我想知道是否有任何方法可以保存到这个文件夹,同时还可以设置我自己的文件名。并且这种方法是否会在其他设备上产生不可预见的问题。

编辑: 对于任何感兴趣的人,这是我当前的代码,基于@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);
}

This is supposed to save to the 'Pictures' folder

getExternalStorageDirectory() return 是外部存储的根,而不是其中的某个位置(例如,Pictures/DCIM/Movies/)。

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

这将为您提供正确的目录,或者设备认为正确的目录(通常在外部存储中为当前用户命名为 Pictures/)。

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

11 有点不寻常,getAbsolutePath() 没有 return 带有 file:// 方案的东西,但除此之外似乎是正确的。

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

我不知道你是怎么确定的。

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

从您的第一个样本开始,切换到 DIRECTORY_PICTURES。那么:

  • outputStream成为FileOutputStream

  • flush()之后和close()

  • 之前调用outputStream.getFD().sync()
  • 使用 MediaScannerConnection 及其 scanFile() 方法安排 MediaStore 对图像进行索引,因此它可用于设备上的图库、桌面 OS 文件管理器等

随着 Android 6.0 Marshmallow (API >= 23),Google 引入了新的权限模型。

您必须在 运行 时请求许可:

String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, WRITE_REQUEST_CODE);

并在 onRequestPermissionsResult() 中处理结果,

File path = Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_PICTURES);
File file = new File(path, "db.JPG");
try {
    file.createNewFile();
}
catch (IOException e) {
    Toast.makeText(this.getApplicationContext(),
    "createNewFile:"+e.toString(),
    Toast.LENGTH_LONG).show();
}

然后

if (Build.VERSION.SDK_INT < 19) {
    sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
    Uri.parse("file://" + filename)));
}
else {
    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
    Uri.parse("file://" + filename)));
}

在画廊中展示。