安卓位图转换为Base64字符串

167

如何将大型位图(使用手机相机拍摄的照片)转换为Base64字符串?


你想如何对图像进行编码? - Ted Hopp
你问错了问题。用手机相机拍摄的照片存储为JPEG格式,而不是位图。你只需要解码JPEG格式为位图以便显示。如果你按照我下面的答案操作,就会减少OutOfMemory错误和不必要的处理。 - Carson Holzheimer
7个回答

369

使用以下方法将位图转换为字节数组:

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream .toByteArray();

使用以下方法从字节数组编码为Base64:

String encoded = Base64.encodeToString(byteArray, Base64.DEFAULT);

5
@SachinGurnani - 这是因为logcat显示的字符串长度有限,并且在达到限制后就被截断了。这就是原因。 - Pankaj Singh
感谢您的回复,Pankaj。我当天就解决了这个问题。 - Sachin Gurnani
1
这应该在AsyncTask中完成吗?还是在主线程中完成也可以? - n3wb
这个解决方案有两个问题。首先,你不应该在第一时间解码jpeg,其次,你也不应该将byteArray存储在内存中。对于大型照片,这将会引发OOM错误。 - Carson Holzheimer
2
如果您不想在Base64字符串中包含\n,则可以使用Base64.NO_WRAP - NecipAllef
显示剩余6条评论

36
我有一个快速的解决方案。只需要创建一个名为ImageUtil.java的文件。
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;
import java.io.ByteArrayOutputStream;

public class ImageUtil
{
    public static Bitmap convert(String base64Str) throws IllegalArgumentException
    {
        byte[] decodedBytes = Base64.decode(
            base64Str.substring(base64Str.indexOf(",")  + 1),
            Base64.DEFAULT
        );

        return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
    }

    public static String convert(Bitmap bitmap)
    {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);

        return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
    }

}

使用方法:

Bitmap bitmap = ImageUtil.convert(base64String);

或者

String base64String = ImageUtil.convert(bitmap);

1
这个 base64Str.substring(base64Str.indexOf(",") + 1) 是在做什么?我没有看到我的字符串中有“,”。 - A1m
嗨,当我看到base64字符串的输出时,它会渲染出换行符,我该怎么解决呢?因为我需要在内联模式下获取它。 - Fernando Torres

14

Jeet的答案存在一个问题,即你将图像的所有字节加载到一个字节数组中,这可能会在低端设备上导致应用程序崩溃。相反,我会先将图像写入文件,然后使用Apache的Base64InputStream类读取它。然后,您可以直接从该文件的InputStream创建Base64字符串。它看起来像这样:

//Don't forget the manifest permission to write files
final FileOutputStream fos = new FileOutputStream(yourFileHere); 
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);

fos.close();

final InputStream is = new Base64InputStream( new FileInputStream(yourFileHere) );

//Now that we have the InputStream, we can read it and put it into the String
final StringWriter writer = new StringWriter();
IOUtils.copy(is , writer, encoding);
final String yourBase64String = writer.toString();

如您所见,上述解决方案直接使用流进行操作,避免了将所有字节加载到变量中的需求,因此使内存占用更低,不太可能在低端设备上崩溃。仍然存在这样一个问题,即将Base64字符串本身放入String变量中不是一个好主意,因为它可能会导致OutOfMemory错误。但至少通过消除字节数组,我们已将内存消耗减半。
如果您想跳过写入文件的步骤,则必须将OutputStream转换为InputStream,这并不是很直接的操作(您必须使用PipedInputStream,但这有点复杂,因为两个流必须始终位于不同的线程中)。

3
这里的“encoding”是什么意思? - Animesh Mangla

9

现在大多数人使用Kotlin而不是Java,这里是用Kotlin将位图转换为Base64字符串的代码。

import java.io.ByteArrayOutputStream

private fun encodeImage(bm: Bitmap): String? {
        val baos = ByteArrayOutputStream()
        bm.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        val b = baos.toByteArray()
        return Base64.encodeToString(b, Base64.DEFAULT)
    }

7

首先,尝试将您的图片按要求缩放到指定的宽度和高度,只需将原始位图、所需宽度和所需高度传递给以下方法即可获得缩放后的位图:

例如:Bitmap scaledBitmap = getScaledBitmap(originalBitmap, 250, 350);

private Bitmap getScaledBitmap(Bitmap b, int reqWidth, int reqHeight)
{
    int bWidth = b.getWidth();
    int bHeight = b.getHeight();

    int nWidth = bWidth;
    int nHeight = bHeight;

    if(nWidth > reqWidth)
    {
        int ratio = bWidth / reqWidth;
        if(ratio > 0)
        {
            nWidth = reqWidth;
            nHeight = bHeight / ratio;
        }
    }

    if(nHeight > reqHeight)
    {
        int ratio = bHeight / reqHeight;
        if(ratio > 0)
        {
            nHeight = reqHeight;
            nWidth = bWidth / ratio;
        }
    }

    return Bitmap.createScaledBitmap(b, nWidth, nHeight, true);
}

现在只需将您缩放后的位图传递给以下方法,并获得返回的base64字符串:
例如:String base64String = getBase64String(scaledBitmap);
private String getBase64String(Bitmap bitmap)
{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

    byte[] imageBytes = baos.toByteArray();

    String base64String = Base64.encodeToString(imageBytes, Base64.NO_WRAP);

    return base64String;
}

将base64字符串解码为位图图像:

byte[] decodedByteArray = Base64.decode(base64String, Base64.NO_WRAP);
Bitmap decodedBitmap = BitmapFactory.decodeByteArray(decodedByteArray, 0, decodedString.length);

我找到了解决我的问题的答案:在我的Base64编码位图中,我遇到了“非法字符a”的错误,使用Base64.NO_WRAP修复了这个问题。 - pirogtm

5

所有这些答案都是低效的,因为它们不必要地解码为位图,然后重新压缩位图。在Android上拍照时,照片以jpeg格式存储在临时文件中,您可以在遵循android文档时指定该文件。

您应该直接将文件转换为Base64字符串。以下是一些易于复制粘贴的方法(使用Kotlin)。请注意,您必须关闭base64FilterStream以完全刷新其内部缓冲区。

fun convertImageFileToBase64(imageFile: File): String {

    return FileInputStream(imageFile).use { inputStream ->
        ByteArrayOutputStream().use { outputStream ->
            Base64OutputStream(outputStream, Base64.DEFAULT).use { base64FilterStream ->
                inputStream.copyTo(base64FilterStream)
                base64FilterStream.close()
                outputStream.toString()
            }
        }
    }
}

作为额外的福利,由于绕过重新压缩,您的图像质量应该会略微提高。

-3
请使用以下代码...
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;
import java.io.ByteArrayOutputStream;

public class ImageUtil 
{ 
    public static Bitmap convert(String base64Str) throws IllegalArgumentException 
    { 
        byte[] decodedBytes = Base64.decode( base64Str.substring(base64Str.indexOf(",") + 1), Base64.DEFAULT );
        return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
    } 

    public static String convert(Bitmap bitmap) 
    { 
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
        return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
    }
}

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