如何高效地将字节数组进行Base64编码?

79

我有一个 byte[],想要将它转换成 base64 编码的方式中最有效率的方法。

问题在于内置的 .Net 方法 Convert.FromBase64CharArray 需要一个 char[] 作为输入,而为了再次将其转换为 base64 编码数组,将我的 byte[] 转换为 char[] 看起来相当愚蠢。

是否有更直接的方法来实现呢?

[[编辑:]] 我会更好地解释我的需求 - 我有一个 byte[],需要返回一个新的 base64 编码的 byte[]


你的字节数组采用什么编码方式?ASCII、UTF8等? - Daniel A. White
你写的是想要转换成base64而不是“FromBase64”。 - BlueM
3
也许我漏掉了什么,但是你不能使用 string Convert.ToBase64String(byte[])byte[] Convert.FromBase64String(string) 吗? - Simon Whitehead
@SimonWhitehead 我更新了我的问题,以澄清为什么这还不够好。 - sternr
将字符串转换为字节是另一个故事。使用byte[] Encoding.ASCII.GetBytes(theBase64String)。 - BlueM
9个回答

255

5
你能举例说明一个需要将base64 byte[]转换为byte[]的用例吗?我从未在我的生活中有过这种需求。 - Stephan Schinkel
1
将字符串转换为字节是另一回事。使用 Encoding.ASCII.GetBytes(theString)。 - BlueM
62
这就是为什么你总是要滚动到第二个答案,哈哈。 - DaveMorganTexas
2
@robnick:这是一个巨大的不可行之举。每个被base64编码的二进制数据大小增加了约33%。因此,这并不是一个真正有用的用例。尽管如此,您的用例只需使用byte[] -> string即可涵盖。 - Stephan Schinkel
1
@StephanSchinkel 只需尝试通过ajax请求下载pdf或类似格式的文件,您就会遇到此问题。 :) 例如,您没有打开新选项卡或窗口的选项,当前窗口必须始终不间断且聚焦。我认为,任何类似格式的文件中的33%都不算是与我们这个时代的带宽相比非常糟糕的权衡。我并不是说这是正确的方式。但有时业务要求需要它,因此它是有效的。 - Kristóf Tóth
显示剩余5条评论

23

Base64是一种将字节表示为文本形式(即字符串)的方法。因此,没有所谓的Base64编码的byte[]。您会得到一个base64编码的字符串,然后可以解码回一个byte[]。

但是,如果你想要得到一个byte数组,你可以取base64编码的字符串并将其转换成byte数组,例如:

string base64String = Convert.ToBase64String(bytes);
byte[] stringBytes = Encoding.ASCII.GetBytes(base64String);

然而,这没有任何意义,因为用byte[]表示byte[]的最好方式就是byte[]本身 :)


2
如果你需要将Base64编码形式发送到网络或文件中,那么你需要将其转换为byte[]。不要轻易说某些事情没有意义。 - Amit Bens
1
@AmitBens 当然可以,但如果API接受一个byte[],那么通常可以发送原始字节。也许你在考虑将byte[]嵌入到文本结构(例如JSON)中,然后通过二进制协议(例如TCP)发送更大的结构的情况?你几乎永远不需要直接将字节数组转换为base64,只需再次将其转换回byte[]即可。 - Eren Ersönmez
1
@erenEsonmez - 邮件发送的MIME,对于附件或非ASCII编码的消息使用base64等。你几乎无法在不通过电线发送base64表示的情况下发送电子邮件。 - Amit Bens
1
@ErenErsönmez 还要注意,有些API需要将数据提供为Base64字符串(例如Shopify)。我同意这很愚蠢,但这就是为什么人们可能需要它的原因。 - Maxim Gershkovich
这很危险!我尝试将此逻辑应用于PNG图像,但byte[]返回了错误的字节数组(ASCII和UTF8都是如此)。我建议使用Convert.FromBase64String(base64String)代替。 - GeorgiG
1
@GeorgiG,与您的用例不同,OP对原始字节的往返不感兴趣。请查看问题评论中关于FromBase64String的讨论。 - Eren Ersönmez

6

这里是直接将Base64编码为字节数组的代码(经过测试,性能约为 .Net 实现的 +-10%,但分配的内存只有一半):

    static public void testBase64EncodeToBuffer()
    {
        for (int i = 1; i < 200; ++i)
        {
            // prep test data
            byte[] testData = new byte[i];
            for (int j = 0; j < i; ++j)
                testData[j] = (byte)(j ^ i);

            // test
            testBase64(testData);
        }
    }

    static void testBase64(byte[] data)
    {
        if (!appendBase64(data, 0, data.Length, false).SequenceEqual(System.Text.Encoding.ASCII.GetBytes(Convert.ToBase64String(data)))) throw new Exception("Base 64 encoding failed");
    }

    static public byte[] appendBase64(byte[] data
                              , int offset
                              , int size
                              , bool addLineBreaks = false)
    {
        byte[] buffer;
        int bufferPos = 0;
        int requiredSize = (4 * ((size + 2) / 3));
        // size/76*2 for 2 line break characters    
        if (addLineBreaks) requiredSize += requiredSize + (requiredSize / 38);

        buffer = new byte[requiredSize];

        UInt32 octet_a;
        UInt32 octet_b;
        UInt32 octet_c;
        UInt32 triple;
        int lineCount = 0;
        int sizeMod = size - (size % 3);
        // adding all data triplets
        for (; offset < sizeMod;)
        {
            octet_a = data[offset++];
            octet_b = data[offset++];
            octet_c = data[offset++];

            triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

            buffer[bufferPos++] = base64EncodingTable[(triple >> 3 * 6) & 0x3F];
            buffer[bufferPos++] = base64EncodingTable[(triple >> 2 * 6) & 0x3F];
            buffer[bufferPos++] = base64EncodingTable[(triple >> 1 * 6) & 0x3F];
            buffer[bufferPos++] = base64EncodingTable[(triple >> 0 * 6) & 0x3F];
            if (addLineBreaks)
            {
                if (++lineCount == 19)
                {
                    buffer[bufferPos++] = 13;
                    buffer[bufferPos++] = 10;
                    lineCount = 0;
                }
            }
        }

        // last bytes
        if (sizeMod < size)
        {
            octet_a = offset < size ? data[offset++] : (UInt32)0;
            octet_b = offset < size ? data[offset++] : (UInt32)0;
            octet_c = (UInt32)0; // last character is definitely padded

            triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

            buffer[bufferPos++] = base64EncodingTable[(triple >> 3 * 6) & 0x3F];
            buffer[bufferPos++] = base64EncodingTable[(triple >> 2 * 6) & 0x3F];
            buffer[bufferPos++] = base64EncodingTable[(triple >> 1 * 6) & 0x3F];
            buffer[bufferPos++] = base64EncodingTable[(triple >> 0 * 6) & 0x3F];

            // add padding '='
            sizeMod = size % 3;
            // last character is definitely padded
            buffer[bufferPos - 1] = (byte)'=';
            if (sizeMod == 1) buffer[bufferPos - 2] = (byte)'=';
        }
        return buffer;
    }

7
赞扬你选择了一种较为困难的方法。但我会坚持使用System.Convert。 - Florian Fida
@FlorianFida 当然可以!使用System.Convert的缺点是,当您需要将Base64转换为更大的缓冲区时,您最终会分配额外的缓冲区(如果您的大小> 80k,则很容易污染LOH / GC),但只要代码不是非常频繁地使用,那么保持使用系统方法肯定是安全的 :) - Amit Bens
2
Amit Ben的代码性能很好,但是在你需要将结果转换为字符串或char[](即UTF16)的时候,它会变得相当缓慢。而这种情况经常发生。然后你会发现它比System.Convert.ToBase64String慢两倍。这仅仅是因为最后有一个System.Text.Encoding.ASCII.GetString(bytes)。这对我来说非常出乎意料。 - Roman Pokrovskij

5
byte[] base64EncodedStringBytes = Encoding.ASCII.GetBytes(Convert.ToBase64String(binaryData))

3
根据您的编辑和评论..这会是您想要的吗?
byte[] newByteArray = UTF8Encoding.UTF8.GetBytes(Convert.ToBase64String(currentByteArray));

6
因为我们知道base-64字符串将在ASCII范围内,所以直接使用ASCII编码可能更有效(因为它与UTF8生成的内容相同)。 - Marc Gravell

0
import base64
encoded = base64.b64encode(b'[3.\x01@\xbcr\xa9/$\xc3\ xe1 "')

print(encoded)
data = base64.b64decode(encoded)

print(data)

这种方法对于字符无论如何都是高效的。为了参考,我也包括了空格和双引号。


0
你可以使用 String Convert.ToBase64String(byte[]) 将字节数组编码为 base64 字符串,然后使用 Byte[] Convert.FromBase64String(string) 将结果字符串转换回字节数组。

0

将字节转换为Base64字符串以检索您的图像...

模型属性:

    public byte[] NomineePhoto { get; set; }

    public string NomineePhoneInBase64Str 
    {
        get {
            if (NomineePhoto == null)
                return "";

            return $"data:image/png;base64,{Convert.ToBase64String(NomineePhoto)}";
        } 
    }

在视图中:

   <img style="height:50px;width:50px" src="@item.NomineePhoneInBase64Str" />

-1
    public void ProcessRequest(HttpContext context)
    {
        string constring = ConfigurationManager.ConnectionStrings["SQL_Connection_String"].ConnectionString;
        SqlConnection conn = new SqlConnection(constring);
        conn.Open();
        SqlCommand cmd = new SqlCommand("select image1 from TestGo where TestId=1", conn);
        SqlDataReader dr = cmd.ExecuteReader();
        dr.Read();
        MemoryStream str = new MemoryStream();

        context.Response.Clear();
        Byte[] bytes = (Byte[])dr[0];
        string d = System.Text.Encoding.Default.GetString(bytes);
        byte[] bytes2 = Convert.FromBase64String(d);
        //context.Response.Write(d);
        Image img = Image.FromStream(new MemoryStream(bytes2));
        img.Save(context.Response.OutputStream, ImageFormat.Png);
        context.Response.Flush();
        str.WriteTo(context.Response.OutputStream);
        str.Dispose();
        str.Close();
        conn.Close();
        context.Response.End();
    }

2
我看到你的回答被踩了。请解释一下你的答案,以避免未来社区的踩。 - user10314103

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