Save bitmap to file function

2019-01-14 06:19发布

问题:

I'm trying to save a bitmap to file and a specific directly using a function I've created. It's not working. It crashes after bitmap.compress part (before here3).

    File dir = new File(filepath);

    if(!dir.exists())dir.mkdirs();

    File file = new File(Environment.getExternalStorageDirectory() + filepath, side + ".png");
    FileOutputStream fOut = new FileOutputStream(file);

    bitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut);
    fOut.flush();
    fOut.close();

    System.out.println(filepath);

    bitmap.recycle();
    System.gc();

Error log:

06-29 00:16:38.089: D/AndroidRuntime(3260): Shutting down VM
06-29 00:16:38.089: W/dalvikvm(3260): threadid=1: thread exiting with uncaught exception (group=0xb587f4f0)
06-29 00:16:38.089: E/AndroidRuntime(3260): FATAL EXCEPTION: main
06-29 00:16:38.089: E/AndroidRuntime(3260): java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=0, result=-1, data=Intent { act=android.intent.action.VIEW dat=content://org.openintents.filemanager/mimetype//mnt/sdcard/download/02977_awreckedboatintheocean_1280x1024.jpg }} to activity {com.polygonattraction.testbirds/com.polygonattraction.testbirds.functions.SelectImageSource}: java.lang.IllegalStateException: Can't compress a recycled bitmap
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.app.ActivityThread.deliverResults(ActivityThread.java:2532)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.app.ActivityThread.handleSendResult(ActivityThread.java:2574)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.app.ActivityThread.access$2000(ActivityThread.java:117)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:961)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.os.Handler.dispatchMessage(Handler.java:99)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.os.Looper.loop(Looper.java:130)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.app.ActivityThread.main(ActivityThread.java:3683)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at java.lang.reflect.Method.invokeNative(Native Method)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at java.lang.reflect.Method.invoke(Method.java:507)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at dalvik.system.NativeStart.main(Native Method)
06-29 00:16:38.089: E/AndroidRuntime(3260): Caused by: java.lang.IllegalStateException: Can't compress a recycled bitmap
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.graphics.Bitmap.checkRecycled(Bitmap.java:180)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.graphics.Bitmap.compress(Bitmap.java:581)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at com.polygonattraction.testbirds.functions.Functions.SaveToFile(Functions.java:144)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at com.polygonattraction.testbirds.functions.SelectImageSource.onActivityResult(SelectImageSource.java:113)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.app.Activity.dispatchActivityResult(Activity.java:3908)
06-29 00:16:38.089: E/AndroidRuntime(3260):     at android.app.ActivityThread.deliverResults(ActivityThread.java:2528)

回答1:

  1. You need an appropriate permission in manifest.xml:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
  2. out.flush() check the out is not null..

  3. String file_path = Environment.getExternalStorageDirectory().getAbsolutePath() + 
                                "/PhysicsSketchpad";
    File dir = new File(file_path);
    if(!dir.exists())
        dir.mkdirs();
    File file = new File(dir, "sketchpad" + pad.t_id + ".png");
    FileOutputStream fOut = new FileOutputStream(file);
    
    bmp.compress(Bitmap.CompressFormat.PNG, 85, fOut);
    fOut.flush();
    fOut.close();
    


回答2:

I think this is much better answer.

two example works for me, for your reference.

Bitmap bitmap = Utils.decodeBase64(base64);
    try{
        File file = new File(filePath);
        FileOutputStream fOut = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut);
        fOut.flush();
        fOut.close();}
    catch (Exception e) {
        e.printStackTrace();
        LOG.i(null, "Save file error!");
        return false;
}

and this one

    Bitmap savePic = Utils.decodeBase64(base64);
    File file = new File(filePath);
    File path = new File(file.getParent());

    if (savePic != null) {
        try {
            // build directory
            if (file.getParent() != null && !path.isDirectory()) {
                path.mkdirs();
            }
            // output image to file
            FileOutputStream fos = new FileOutputStream(filePath);
            savePic.compress(Bitmap.CompressFormat.PNG, 90, fos);
            fos.close();
            ret = true;
        } catch (Exception e) {
            e.printStackTrace();
        }

    } else {
        LOG.i(TAG, "savePicture image parsing error");
    }

I think this is much better answer.



回答3:

Here is the function which help you

private void saveBitmap(Bitmap bitmap,String path){
        if(bitmap!=null){
            try {
                FileOutputStream outputStream = null;
                try {
                    outputStream = new FileOutputStream(path); //here is set your file path where you want to save or also here you can set file object directly

                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); // bitmap is your Bitmap instance, if you want to compress it you can compress reduce percentage
                    // PNG is a lossless format, the compression factor (100) is ignored
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (outputStream != null) {
                            outputStream.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }    
        }
    }


回答4:

In kotlin :

private fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) {
    outputStream().use{ out->
        bitmap.compress(format, quality, out)
        out.flush()
    }
}

usage example:

File(exportDir, "map.png").writeBitmap(bitmap, Bitmap.CompressFormat.PNG, 85)


回答5:

implementation save bitmap and load bitmap directly. fast and ease on mfc class

void CMRSMATH1Dlg::Loadit(TCHAR *destination, CDC &memdc)
{
CImage img;
PBITMAPINFO bmi;
BITMAPINFOHEADER Info;
BITMAPFILEHEADER bFileHeader;
CBitmap bm;     
CFile file2;
file2.Open(destination, CFile::modeRead | CFile::typeBinary);
file2.Read(&bFileHeader, sizeof(BITMAPFILEHEADER));
file2.Read(&Info, sizeof(BITMAPINFOHEADER));    
BYTE ch;
int width = Info.biWidth;
int height = Info.biHeight;
if (height < 0)height = -height;
int size1 = width*height * 3;
int size2 = ((width * 24 + 31) / 32) * 4 * height;
int widthnew = (size2 - size1) / height;
BYTE * buffer = (BYTE *)GlobalAlloc(GPTR, size2);
//////////////////////////
HGDIOBJ old;
unsigned char alpha = 0;    
int z = 0;  
z = 0;
int gap = (size2 - size1) / height;
for (int y = 0;y < height;y++)
{
    for (int x = 0;x < width*3;x++)
    {
        file2.Read(&ch, 1);
        buffer[z] = ch;
        z++;
    }
    for (int z1 = 0;z1 <gap;z1++)
    {
        file2.Read(&ch,1);
    }
}
bm.CreateCompatibleBitmap(&memdc, width, height);
bm.SetBitmapBits(size1,buffer);
old = memdc.SelectObject(&bm);   
///////////////////////////  
 //bm.SetBitmapBits(size1, buffer);     
 GetDC()->BitBlt(1, 95, width, height, &memdc, 0, 0, SRCCOPY);
 memdc.SelectObject(&old);
 bm.DeleteObject();
 GlobalFree(buffer);     
 file2.Close();
 }
 void CMRSMATH1Dlg::saveit(CBitmap &bit1, CDC &memdc, TCHAR *destination)
  {     
BITMAP bm;
PBITMAPINFO bmi;
BITMAPINFOHEADER Info;
BITMAPFILEHEADER bFileHeader;
CFile file1;
            CSize size = bit1.GetBitmap(&bm);
 int z = 0;  
 BYTE ch = 0;
 size.cx = bm.bmWidth;
 size.cy = bm.bmHeight;
 int width = size.cx;    
 int size1 = (size.cx)*(size.cy);
 int size2 = size1 * 3;  
 size1 = ((size.cx * 24 + 31) / 32) *4* size.cy;     
 BYTE * buffer = (BYTE *)GlobalAlloc(GPTR, size2);               
 bFileHeader.bfType = 'B' + ('M' << 8); 
 bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
 bFileHeader.bfSize = bFileHeader.bfOffBits + size1;
 bFileHeader.bfReserved1 = 0;
 bFileHeader.bfReserved2 = 0;   
 Info.biSize = sizeof(BITMAPINFOHEADER);
 Info.biPlanes = 1;
 Info.biBitCount = 24;//bm.bmBitsPixel;//bitsperpixel///////////////////32
 Info.biCompression = BI_RGB;
 Info.biWidth =bm.bmWidth;
 Info.biHeight =-bm.bmHeight;///reverse pic if negative height
 Info.biSizeImage =size1;
 Info.biClrImportant = 0;
 if (bm.bmBitsPixel <= 8)
 {
     Info.biClrUsed = 1 << bm.bmBitsPixel;
 }else
 Info.biClrUsed = 0;
 Info.biXPelsPerMeter = 0;
 Info.biYPelsPerMeter = 0;
 bit1.GetBitmapBits(size2, buffer);      
 file1.Open(destination, CFile::modeCreate | CFile::modeWrite |CFile::typeBinary,0);
 file1.Write(&bFileHeader, sizeof(BITMAPFILEHEADER));
 file1.Write(&Info, sizeof(BITMAPINFOHEADER));
 unsigned char alpha = 0;    
 for (int y = 0;y<size.cy;y++)
 {
     for (int x = 0;x<size.cx;x++)
     {
         //for reverse picture below
         //z = (((size.cy - 1 - y)*size.cx) + (x)) * 3;          
     z = (((y)*size.cx) + (x)) * 3;
     file1.Write(&buffer[z], 1);
     file1.Write(&buffer[z + 1], 1);
     file1.Write(&buffer[z + 2], 1);
     }               
     for (int z = 0;z < (size1 - size2) / size.cy;z++)
     {
         file1.Write(&alpha, 1);
     }
 }   
 GlobalFree(buffer);     
 file1.Close();  
 file1.m_hFile = NULL;
        }