将位图保存到指定位置

524
我正在编写一个函数,用于从Web服务器下载图像,将其显示在屏幕上,并且如果用户想要保留该图像,则将其保存在SD卡的某个文件夹中。是否有一种简单的方法可以将位图保存到我选择的文件夹中?
我的问题是,我可以下载图像并将其显示为位图。我找到的唯一保存图像到特定文件夹的方法是使用FileOutputStream,但这需要一个字节数组。我不确定如何进行转换(如果这是正确的方式),以便我可以使用FileOutputStream来写入数据。
我另一个选择是使用MediaStore:
MediaStore.Images.Media.insertImage(getContentResolver(), bm,
    barcodeNumber + ".jpg Card Image", barcodeNumber + ".jpg Card Image");

这个方法可以成功地将文件保存到SD卡,但不允许你自定义文件夹。


正是我在我的应用程序中所做的。我从Web服务器下载一个大图像,对其进行操作,然后通过mImage.setImageBitmap(_result.getBitmap());直接将位图加载到ImageView中,在我的onTaskComplete()回调中。现在,如果用户希望通过长按上下文菜单将文件保存到本地,我必须允许他们这样做。我应该能够使用下面的解决方案。不过我想知道,你是否发现了更好的方法? - wired00
这里有一种优雅的方法:https://dev59.com/GG855IYBdhLWcg3wlFWP - Chepech
请查看我在此处的答案:https://dev59.com/pF0a5IYBdhLWcg3wipTk#68110559 - Amr
19个回答

985
try (FileOutputStream out = new FileOutputStream(filename)) {
    bmp.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance
    // PNG is a lossless format, the compression factor (100) is ignored
} catch (IOException e) {
    e.printStackTrace();
}

12
我也压缩了图像,但是压缩比例为100%。然而,当我在画布上获取图像时,它非常小。有什么原因吗? - AZ_
3
这不会改变图像的大小,只会改变格式和(可能)质量。值得注意的是,上面示例中的压缩质量90在保存为PNG时不会产生任何影响,但对于JPEG来说则有所区别。在JPEG的情况下,您可以选择0到100之间的任何数字。 - plowman
53
必须重新压缩吗?我只想保存原始图像。 - Hein du Plessis
2
肯定更好的做法是使用上下文来获取正确的路径,以确保位置正确,即使它发生了变化:FileOutputStream out = context.openFileOutput(filename, Context.MODE_PRIVATE);其中private不是SDCARD位置。 - Dev2rights
2
我该如何获取图像? - Sheychan
显示剩余9条评论

147

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

这里有一个通过 getImageBitmap(myurl) 获得的 Bitmap 实例的示例,可以用 85% 的压缩率将其压缩为 JPEG 格式:

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

@JoaquinG 有没有什么理由不使用 fOut.flush(),可以省略它吗? - Niklas
@Niklas 我认为你可以省略 flush。 - JoaquinG
2
您应该将“85%的压缩率”改为“85%的质量率”,以减少歧义。我会将“85%的压缩率”解释为“15%的质量”,但是Bitmap.compress的int参数指定了质量。 - Tim Cooke
请问您能否发布getImageBitmap(myurl)方法? - Aashish Kumar

39
outStream = new FileOutputStream(file);

在AndroidManifest.xml中没有权限会抛出异常(至少在os2.2中):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

14
如果你的文件绝对路径是内部路径,那么不行吗? - Blundell

31

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

9
你称之为“pippo.jpg”,但你却使用PNG压缩方式。 - Ralphleon
1
压缩格式应为 .JPEG,如果您想更改位图的质量。在 PNG 格式中无法更改质量。 - Mustafa Güven

13

这是保存位图至文件的示例代码:

public static File savebitmap(Bitmap bmp) throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.JPEG, 60, bytes);
    File f = new File(Environment.getExternalStorageDirectory()
            + File.separator + "testimage.jpg");
    f.createNewFile();
    FileOutputStream fo = new FileOutputStream(f);
    fo.write(bytes.toByteArray());
    fo.close();
    return f;
}

现在调用该函数将位图保存到内部存储器。

File newfile = savebitmap(bitmap);

希望这能对您有所帮助。 愉快的编码生活。


13

一些格式(如PNG)是无损的,将忽略质量设置。


3
PNG仍然是一种压缩格式。质量设置是否不会修改压缩质量? - Tamás Barta
文档中指出(我强调):压缩器的提示,0-100。0表示为小尺寸进行压缩,100表示为最大质量进行压缩。一些格式(如PNG是无损的)将忽略质量设置 - Stephan Henningsen

13

我知道这个问题很旧,但是现在我们可以在不需要WRITE_EXTERNAL_STORAGE权限的情况下实现相同的结果。我们可以使用File provider。

private fun storeBitmap(bitmap: Bitmap, file: File){
        requireContext().getUriForFile(file)?.run {
            requireContext().contentResolver.openOutputStream(this)?.run {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, this)
                close()
            }
        }
    }

如何从提供商那里检索文件?

fun Context.getUriForFile(file: File): Uri? {
        return FileProvider.getUriForFile(
            this,
            "$packageName.fileprovider",
            file
        )
    }

还要别忘了在Android清单中注册provider

<provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
    </provider>

运行得非常好!!!足以分享一张照片。 - Javier

8
Bitmap bbicon;

bbicon=BitmapFactory.decodeResource(getResources(),R.drawable.bannerd10);
//ByteArrayOutputStream baosicon = new ByteArrayOutputStream();
//bbicon.compress(Bitmap.CompressFormat.PNG,0, baosicon);
//bicon=baosicon.toByteArray();

String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
OutputStream outStream = null;
File file = new File(extStorageDirectory, "er.PNG");
try {
    outStream = new FileOutputStream(file);
    bbicon.compress(Bitmap.CompressFormat.PNG, 100, outStream);
    outStream.flush();
    outStream.close();
} catch(Exception e) {

}

1
如果您在“compress”方法中传递了outStream,则无需刷新它。该方法会代替您执行。 - dharmendra

8

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


即使它被忽略了,它应该是100。如果有一天压缩格式被更改为松散的格式,那么图像将最接近于松散的格式。还要注意,如果您有抽象此调用的代码,则可能更重要。 - ddcruver
2
100%不是无损的JPEG格式,顺便说一下。您可以通过重复加载和保存位图来验证此内容。 - user153275

6

我也想保存一张图片。但我的问题是,我想从我绘制的位图中保存它。

我是这样做的:

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
            case R.id.save_sign:      

                myView.save();
                break;

            }
            return false;    

    }

public void save() {
            String filename;
            Date date = new Date(0);
            SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMddHHmmss");
            filename =  sdf.format(date);

            try{
                 String path = Environment.getExternalStorageDirectory().toString();
                 OutputStream fOut = null;
                 File file = new File(path, "/DCIM/Signatures/"+filename+".jpg");
                 fOut = new FileOutputStream(file);

                 mBitmap.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
                 fOut.flush();
                 fOut.close();

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

            }catch (Exception e) {
                e.printStackTrace();
            }

 }

您的保存方法只对我有效..浪费了我几个小时..非常感谢您。 - Mohammed Sufian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接