我正在研究从web服务器下载图像的功能,在屏幕上显示它,如果用户希望保持图像,将其保存在SD卡的某个文件夹中。有没有一种简单的方法来获取位图并将其保存到我选择的SD卡文件夹中?

我的问题是,我可以下载图像,在屏幕上显示为位图。我能够找到的将图像保存到特定文件夹的唯一方法是使用FileOutputStream,但这需要一个字节数组。我不知道如何转换(如果这是正确的方式)从位图到字节数组,所以我可以使用FileOutputStream写入数据。

我的另一个选择是使用MediaStore:

MediaStore.Images.Media.insertImage(getContentResolver(), bm,
    barcodeNumber + ".jpg Card Image", barcodeNumber + ".jpg Card Image");

它可以很好地保存到SD卡,但不允许您自定义文件夹。


当前回答

在onActivityResult:

String filename = "pippo.png";
File sd = Environment.getExternalStorageDirectory();
File dest = new File(sd, filename);

Bitmap bitmap = (Bitmap)data.getExtras().get("data");
try {
     FileOutputStream out = new FileOutputStream(dest);
     bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
     out.flush();
     out.close();
} catch (Exception e) {
     e.printStackTrace();
}

其他回答

在Android 4.4 Kitkat之后,截至2017年,Android 4.4及以下的份额约为20%并且正在下降,使用File类和getExternalStorageDirectory()方法保存到SD卡是不可能的。这个方法返回你的设备内部内存和图像保存为每个应用程序可见。你也可以只保存你的应用程序私有的图像,当用户使用openFileOutput()方法删除你的应用程序时删除。

从Android 6.0开始,您可以将SD卡格式化为内部内存,但只能对您的设备私有。(如果你格式化SD车作为内部存储器,只有你的设备可以访问或看到它的内容)你可以使用其他答案保存到SD卡,但如果你想使用一个可移动的SD卡,你应该阅读下面我的回答。

您应该使用存储访问框架获取uri到activity的onActivityResult方法来获取用户选择的文件夹,并添加检索持久权限,以便用户重新启动设备后能够访问文件夹。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK) {

        // selectDirectory() invoked
        if (requestCode == REQUEST_FOLDER_ACCESS) {

            if (data.getData() != null) {
                Uri treeUri = data.getData();
                tvSAF.setText("Dir: " + data.getData().toString());
                currentFolder = treeUri.toString();
                saveCurrentFolderToPrefs();

                // grantUriPermission(getPackageName(), treeUri,
                // Intent.FLAG_GRANT_READ_URI_PERMISSION |
                // Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

                final int takeFlags = data.getFlags()
                        & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                // Check for the freshest data.
                getContentResolver().takePersistableUriPermission(treeUri, takeFlags);

            }
        }
    }
}

现在,保存保存文件夹到共享首选项,而不是每次你想保存图像时都要求用户选择文件夹。

你应该使用DocumentFile类来保存你的图像,而不是File或ParcelFileDescriptor,要了解更多信息,你可以检查这个线程保存图像到SD卡压缩(CompressFormat.JPEG, 100, out);方法和DocumentFile类。

为什么不调用Bitmap.compress方法100(这听起来像是无损的)?

在onActivityResult:

String filename = "pippo.png";
File sd = Environment.getExternalStorageDirectory();
File dest = new File(sd, filename);

Bitmap bitmap = (Bitmap)data.getExtras().get("data");
try {
     FileOutputStream out = new FileOutputStream(dest);
     bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
     out.flush();
     out.close();
} catch (Exception e) {
     e.printStackTrace();
}

您应该使用Bitmap.compress()方法将位图保存为文件。它将压缩(如果所使用的格式允许的话)您的图片并将其推入OutputStream。

下面是一个通过getImageBitmap(myurl)获得的位图实例,可以压缩为JPEG,压缩率为85%:

// Assume block needs to be inside a Try/Catch block.
String path = Environment.getExternalStorageDirectory().toString();
OutputStream fOut = null;
Integer counter = 0;
File file = new File(path, "FitnessGirl"+counter+".jpg"); // the File to save , append increasing numeric counter to prevent files from getting overwritten.
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(); // Not really required
fOut.close(); // do not forget to close the stream

MediaStore.Images.Media.insertImage(getContentResolver(),file.getAbsolutePath(),file.getName(),file.getName());

有些格式,比如无损的PNG,会忽略质量设置。