Java, Python 两种形式的 base64encode

在用 Python 写 Web 服务端代码时,用 base64.encodebytes() 函数对字符串进行编码,然后在 Java 端用 Base64.getDecoder().decode() 时无法解码,难道 base64 编码在两种语言间还有这等差异。Google 一下,得到的答案是在 Java 端要用 Base64.getMimeDecoder().decode() 函数解码。这一问题算是解决了, 不过后来又在 Python 写的 AWS Lambda 中输出

return {
    "statusCode": 200,
    "body": base64.encodebytes(b"short message"),
    "isBase64Encoded": True
}

以 AWS Lambda functionURL 的方式来访问,对于 body 中的小字符串是没问题,一旦 body 够大时在 Postman 或 curl 命令中无法直接展示出来,用 curl --output a.out 存成本地文件,打开后看到的是带换行的格式

H4sIAAZi7GYC/+19WXfcOLLmX+HxwxzXOS6b2AhiprvnyFtZt7yoJbdr6r74UEpKyq5UpjoXL/fX
D8AlkysIkCFmpo2H7pJJEBkAAsCHQMQXf/t6HX/YrO83a++P168+xcvVdDH/+yP6lOGnzEciCNHn
gD7yTufT9evpLD6L1rd/f3Q6n03nsXexXk7nN4+8F4v59fRms4zW8uNtHdjH9KkfPEXoM6JPsf8U
......

这就是问题的所在,由 base64.encodebytes() 产生的 'body' 其实连 API Gateway 也无法自动解码出来,所以 base64 编码的内容才直接送到了客户端,原来 API Gateway 在看到 "isBase64Encoded": True 后会解码后送给请求端。

ChatGPT 的解决是在 Python 端用 base64.b64encode() 代替 base64.encodebytes(), 所以在 Python 和 Java 端有下面的对应关系

base64.b64encode()        <->   Base64.getDecoder().encode()
base64.encodebytes()      <->  Base64.getMimeDecoder().decode()

相应的

Base64.getEncoder().encode()      <->  base64.b64decode()  
Base64.getMimeEncoder().encode()      <->  base64.decodebytes()

那么 Python 的 base64.b64encode() 与 base64.encodebytes() 有什么区别呢?或者说 Java 的 Base64.getEncoder().encode() 和 Base64.getMimeEncoder().encode() 有何不同?

用一段代码来测试下  test.py

当字符串较短时,如输入

python a.py 10
source str:
hellohellohellohellohellohellohellohellohellohello
----base64.encodebytes()----
aGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG8=

----base64.b64encode()----
aGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG8=

而当字符串较大时

python a.py 50
source str:
hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
----base64.encodebytes()----
aGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hl
bGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxs
b2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9o
ZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVs
bG9oZWxsb2hlbGxvaGVsbG9oZWxsbw==

----base64.b64encode()----
aGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsbw==

有没有对 base64.encodebytes() 这种格式很熟悉的感觉,就是像软件的注册文件的样式。base64.encodebytes() 会对产生的编码中插入换行符使得显示的时候更整洁些,看字符串内容的话会有许多的 \r\n  换行字符,当等编码的字符串较短时(编码后未超过一行),base64.encodebytes() 与 base64.b64encode() 生成的结果是一样的。base64.b64encode() 才是原滋原味的 base64 编码, 它们之间除了格式化用的换行外没有别的区别了,所以用 base64.encodebytes() 编码的字符串手工去掉换行符,然后用 Base64.getDecoder().decode() 也可以解码出来。

我看阅读 Java 的 Base64.getMimeEncoder() 的源码

然后是 RFC2045 的相关定义

每行宽度 76 个字符,用 \r\n 分割,也就是前面看到的 base64.encodebytes() 产生长字符串的样子。

最后重复一遍上面的

base64.b64encode()        <->   Base64.getDecoder().encode()                                // 原味的 base64
base64.encodebytes()      <->  Base64.getMimeDecoder().decode()                      // 插入换行 的 base64

相应的

Base64.getEncoder().encode()      <->  base64.b64decode()                                  // 原味的 base64
Base64.getMimeEncoder().encode()      <->  base64.decodebytes()                     // 插入换行 的 base64

想当然 Java 的 Base64.getMimeEncoder().encode() 编码出来长字符串也应该是会插入换行符的,何不快速体验一下呢?

jshell> System.out.println(new String(Base64.getMimeEncoder().encode("hello".repeat(50).getBytes())));
aGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hl
bGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxs
b2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9o
ZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVsbG9oZWxsb2hlbGxvaGVs
bG9oZWxsb2hlbGxvaGVsbG9oZWxsbw==

带附加换行符的 base64 只能用 base64.encodebytes() 和 Base64.getMimeDecoder().decode() 来解码,也就是说任何时候都可以用 base64.encodebytes() 或 Base64.getMimeDecoder().decode() 这两个函数进行 base64 解码,但编码的时候需看仔细。

本文链接 https://yanbin.blog/java-python-two-styles-base64encode/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments