将位图保存到位置


469

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

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

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

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

保存到SD卡的效果很好,但不允许自定义文件夹。


正是我在应用程序中所做的。我下载了一个大型图像形式的Web服务器来对其进行操作,然后通过回调将位图直接加载到imageview mImage.setImageBitmap(_result.getBitmap());onTaskComplete()。我现在必须允许用户通过长按上下文菜单在本地保存文件。我应该可以使用下面的解决方案。我想知道的是,您找到了更好的方法吗?
有线00年

这里有做这件事的方式优雅的方式:stackoverflow.com/questions/4263375/...
Chepech

Answers:


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

11
我还压缩了图像,但是将其压缩到100%,当我将图像放入画布中时,它很小。任何原因?
AZ_

3
@Aizaz这不会更改图像的大小,只会更改格式和(可能)质量。还值得注意的是90,在上面的示例中,压缩质量()在另存为PNG时不会产生任何影响,但会对JPEG产生影响。对于JPEG,您可以选择0到100之间的任何数字
。– plowman

1
应当注意,以100%质量保存.JPEG的这种方法实际上将保存与Web原始图像不同的图像(至少会占用更多空间),请考虑使用其他方法。
Warpzit 2011年

42
是否需要重新压缩?我只想保存原始图像。
海因杜普莱西斯(Hein du Plessis)2012年

2
@HeinduPlessis不必,但您可能应该。保存原始位图将占用更多空间,具体取决于格式(例如ARGB_4444与ARGB_8888)。
irwinb 2013年

134

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

这是通过获得的Bitmap实例的示例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());

@JoaquinG是否有任何理由fOut.flush()可以忽略它?
尼古拉斯2015年

@Niklas我认为您可以省略同花顺。
JoaquinG

1
您应将措词从“ 85%的压缩率”更改为“ 85%的质量率”,以减少歧义。我会将“ 85%的压缩率”解释为“ 15%的质量”,但是int参数Bitmap.compress指定了质量。
蒂姆·库克

您能否发布方法getImageBitmap(myurl)
Aashish

38
outStream = new FileOutputStream(file);

会在未经AndroidManifest.xml许可的情况下引发异常(至少在os2.2中):

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

10
如果您的文件absolutePath是内部路径,那不是吗?
Blundell 2012年

24

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

7
您将其称为“ pippo.jpg”,但您使用的是PNG压缩
Ralphleon

1
如果要更改位图的质量,压缩格式应为.JPEG。无法以PNG格式更改质量。
穆斯塔法·古文(MustafaGüven)

13

某些格式(如无损PNG)将忽略质量设置。


2
PNG仍然是压缩格式。质量设置不会改变压缩质量吗?
塔玛斯·巴塔

文档状态(由我突出显示):提示压缩器,0-100。0表示小尺寸压缩,100表示​​最大质量压缩。某些格式(如无损PNG)将忽略质量设置
Stephan Henningsen

10

这是用于将位图保存到文件的示例代码:

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

希望对您有帮助。幸福的编码生活。


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,则无需刷新outStream。该方法将代表您。
dharam 2014年

7

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


即使忽略它也应该是100。如果某天压缩格式更改为宽松格式,则图像将与宽松格式最接近。还要注意,如果您有抽象化此调用的代码,这可能更重要。
ddcruver 2011年

2
JPEG,FWIW并非100%无损。您可以通过重复加载和保存位图来验证这一点。

5

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

我是这样的:

 @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

5

我发现发送PNG和透明度的方式。

String file_path = Environment.getExternalStorageDirectory().getAbsolutePath() +
                    "/CustomDir";
File dir = new File(file_path);
if(!dir.exists())
  dir.mkdirs();

String format = new SimpleDateFormat("yyyyMMddHHmmss",
       java.util.Locale.getDefault()).format(new Date());

File file = new File(dir, format + ".png");
FileOutputStream fOut;
try {
        fOut = new FileOutputStream(file);
        yourbitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut);
        fOut.flush();
        fOut.close();
     } catch (Exception e) {
        e.printStackTrace();
 }

Uri uri = Uri.fromFile(file);     
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
intent.putExtra(android.content.Intent.EXTRA_TEXT, "");
intent.putExtra(Intent.EXTRA_STREAM, uri);

startActivity(Intent.createChooser(intent,"Sharing something")));

这里的值85没有意义,因为PNG是无损的。文档说-“某些格式(如无损PNG)将忽略质量设置”
Minhaz

2

为视频创建视频缩略图。如果视频损坏或不支持该格式,则它可能返回null。

private void makeVideoPreview() {
    Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(videoAbsolutePath, MediaStore.Images.Thumbnails.MINI_KIND);
    saveImage(thumbnail);
}

要将位图保存在sdcard中,请使用以下代码

店铺形象

private void storeImage(Bitmap image) {
    File pictureFile = getOutputMediaFile();
    if (pictureFile == null) {
        Log.d(TAG,
                "Error creating media file, check storage permissions: ");// e.getMessage());
        return;
    } 
    try {
        FileOutputStream fos = new FileOutputStream(pictureFile);
        image.compress(Bitmap.CompressFormat.PNG, 90, fos);
        fos.close();
    } catch (FileNotFoundException e) {
        Log.d(TAG, "File not found: " + e.getMessage());
    } catch (IOException e) {
        Log.d(TAG, "Error accessing file: " + e.getMessage());
    }  
}

获取图像存储路径

/** Create a File for saving an image or video */
private  File getOutputMediaFile(){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this. 
    File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
            + "/Android/data/"
            + getApplicationContext().getPackageName()
            + "/Files"); 

    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            return null;
        }
    } 
    // Create a media file name
    String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
    File mediaFile;
        String mImageName="MI_"+ timeStamp +".jpg";
        mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);  
    return mediaFile;
} 

2

您想将位图保存到您选择的目录中。我制作了一个ImageWorker库,使用户可以加载,保存和转换位图/可绘制对象/ base64图像。

最小SDK-14

前提条件

  • 保存文件将需要WRITE_EXTERNAL_STORAGE权限。
  • 检索文件将需要READ_EXTERNAL_STORAGE权限。

保存位图/可绘制/ Base64

ImageWorker.to(context).
    directory("ImageWorker").
    subDirectory("SubDirectory").
    setFileName("Image").
    withExtension(Extension.PNG).
    save(sourceBitmap,85)

加载位图

val bitmap: Bitmap? = ImageWorker.from(context).
    directory("ImageWorker").
    subDirectory("SubDirectory").
    setFileName("Image").
    withExtension(Extension.PNG).
    load()

实作

添加依赖项

在项目级别Gradle中

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

在应用程序级别中

dependencies {
            implementation 'com.github.ihimanshurawat:ImageWorker:0.51'
    }

您可以在https://github.com/ihimanshurawat/ImageWorker/blob/master/README.md上阅读更多内容。


1

嘿,给这个名字 .bmp

做这个:

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
_bitmapScaled.compress(Bitmap.CompressFormat.PNG, 40, bytes);

//you can create a new file name "test.BMP" in sdcard folder.
File f = new File(Environment.getExternalStorageDirectory()
                        + File.separator + "**test.bmp**")

听起来IM刚到处都是,但是一旦保存到bmp格式中,就尝试一下。


1

调用之前,请确保已创建目录bitmap.compress

new File(FileName.substring(0,FileName.lastIndexOf("/"))).mkdirs();

1

自Android 4.4 Kitkat之后,到2017年,Android 4.4及以下版本的份额约为20%,并且在下降,因此无法使用File类和getExternalStorageDirectory()方法保存到SD卡。此方法返回设备的内部存储器,并且图像保存对每个应用程序可见。您还可以保存仅对应用程序私有的图像,并在用户通过以下方式删除您的应用程序时将其删除openFileOutput()方法。

从Android 6.0开始,您可以将SD卡格式化为内部存储器,但只能用于设备(​​如果将SD car格式化为内部存储器,则只有设备可以访问或查看其内容),您可以使用以下命令将其保存到该SD卡其他答案,但是如果您想使用可移动SD卡,则应阅读以下我的答案。

您应该使用Storage Access Framework来获取活动的uri到文件夹的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);

            }
        }
    }
}

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

您应该使用DocumentFileclass来保存图像,而不是FileParcelFileDescriptor,有关更多信息,您可以检查此线程以使用compress(CompressFormat.JPEG, 100, out);方法和DocumentFile类将图像保存到SD卡。


1

一些新设备不保存位图,所以我解释了一些。

确保您在权限下方添加了

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

并在xml文件夹名称provider_paths.xml下创建一个xml文件

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external_files"
        path="." />
</paths>

并在AndroidManifest下

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

然后只需调用saveBitmapFile(passYourBitmapHere)

public static void saveBitmapFile(Bitmap bitmap) throws IOException {
        File mediaFile = getOutputMediaFile();
        FileOutputStream fileOutputStream = new FileOutputStream(mediaFile);
        bitmap.compress(Bitmap.CompressFormat.JPEG, getQualityNumber(bitmap), fileOutputStream);
        fileOutputStream.flush();
        fileOutputStream.close();
    }

哪里

File getOutputMediaFile() {
        File mediaStorageDir = new File(
                Environment.getExternalStorageDirectory(),
                "easyTouchPro");
        if (mediaStorageDir.isDirectory()) {

            // Create a media file name
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                    .format(Calendar.getInstance().getTime());
            String mCurrentPath = mediaStorageDir.getPath() + File.separator
                            + "IMG_" + timeStamp + ".jpg";
            File mediaFile = new File(mCurrentPath);
            return mediaFile;
        } else { /// error handling for PIE devices..
            mediaStorageDir.delete();
            mediaStorageDir.mkdirs();
            galleryAddPic(mediaStorageDir);

            return (getOutputMediaFile());
        }
    }

和其他方法

public static int getQualityNumber(Bitmap bitmap) {
        int size = bitmap.getByteCount();
        int percentage = 0;

        if (size > 500000 && size <= 800000) {
            percentage = 15;
        } else if (size > 800000 && size <= 1000000) {
            percentage = 20;
        } else if (size > 1000000 && size <= 1500000) {
            percentage = 25;
        } else if (size > 1500000 && size <= 2500000) {
            percentage = 27;
        } else if (size > 2500000 && size <= 3500000) {
            percentage = 30;
        } else if (size > 3500000 && size <= 4000000) {
            percentage = 40;
        } else if (size > 4000000 && size <= 5000000) {
            percentage = 50;
        } else if (size > 5000000) {
            percentage = 75;
        }

        return percentage;
    }

void galleryAddPic(File f) {
        Intent mediaScanIntent = new Intent(
                "android.intent.action.MEDIA_SCANNER_SCAN_FILE");
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);

        this.sendBroadcast(mediaScanIntent);
    }

您是否还有其他信息error handling for PIE devices..,我猜想getOutputMediaFile如果解决方法失败,则递归可能是一个无限循环。
Raimund Wege

0

无需压缩即可将位图保存到您的图库中。

private File saveBitMap(Context context, Bitmap Final_bitmap) {
    File pictureFileDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Your Folder Name");
    if (!pictureFileDir.exists()) {
        boolean isDirectoryCreated = pictureFileDir.mkdirs();
        if (!isDirectoryCreated)
            Log.i("TAG", "Can't create directory to save the image");
        return null;
    }
    String filename = pictureFileDir.getPath() + File.separator + System.currentTimeMillis() + ".jpg";
    File pictureFile = new File(filename);
    try {
        pictureFile.createNewFile();
        FileOutputStream oStream = new FileOutputStream(pictureFile);
        Final_bitmap.compress(Bitmap.CompressFormat.PNG, 100, oStream);
        oStream.flush();
        oStream.close();
        Toast.makeText(Full_Screen_Activity.this, "Save Image Successfully..", Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
        Log.i("TAG", "There was an issue saving the image.");
    }
    scanGallery(context, pictureFile.getAbsolutePath());
    return pictureFile;
}
private void scanGallery(Context cntx, String path) {
    try {
        MediaScannerConnection.scanFile(cntx, new String[]{path}, null, new MediaScannerConnection.OnScanCompletedListener() {
            public void onScanCompleted(String path, Uri uri) {
                Toast.makeText(Full_Screen_Activity.this, "Save Image Successfully..", Toast.LENGTH_SHORT).show();
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
        Log.i("TAG", "There was an issue scanning gallery.");
    }
}

-1

// | == | 从位图创建PNG文件:

void devImjFylFnc(String pthAndFylTtlVar, Bitmap iptBmjVar)
{
    try
    {
        FileOutputStream fylBytWrtrVar = new FileOutputStream(pthAndFylTtlVar);
        iptBmjVar.compress(Bitmap.CompressFormat.PNG, 100, fylBytWrtrVar);
        fylBytWrtrVar.close();
    }
    catch (Exception errVar) { errVar.printStackTrace(); }
}

// | == | 从文件获取Bimap:

Bitmap getBmjFrmFylFnc(String pthAndFylTtlVar)
{
    return BitmapFactory.decodeFile(pthAndFylTtlVar);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.