使用Python 2.7进行分块的base64编码和解码文件
我看过关于base64的Python文档,也在StackOverflow和其他地方看到了一些例子,但我在把base64解码回原始的二进制格式时还是遇到了问题。
我没有收到任何错误提示,所以我觉得不是填充或字符集的问题。我得到的二进制文件比原始文件要小。
我把base64的编码和解码步骤都包括在内,以防这两个步骤中有问题。
这些代码必须在Python 2.7环境下运行。
下面是重现这个问题的脚本。
b64_encode.py
#!/usr/bin/env python2.7
#
# b64_encode.py - must run with python 2.7
# - must process data in chunks to limit memory consumption
# - base64 data must be JSON compatible, i.e.
# use base64 "modern" interface,
# not base64.encodestring() which contains linefeeds
#
import sys, base64
def write_base64_file_from_file(src_fname, b64_fname, chunk_size=8192):
with open(src_fname, 'rb') as fin, open(b64_fname, 'w') as fout:
while True:
bin_data = fin.read(chunk_size)
if not bin_data:
break
print 'bin %s data len: %d' % (type(bin_data), len(bin_data))
b64_data = base64.b64encode(bin_data)
print 'b64 %s data len: %d' % (type(b64_data), len(b64_data))
fout.write(b64_data)
if len(sys.argv) != 2:
print 'usage: %s <bin_fname>' % sys.argv[0]
sys.exit()
bin_fname = sys.argv[1]
b64_fname = bin_fname + '.b64'
write_base64_file_from_file(bin_fname, b64_fname)
b64_decode.py
#!/usr/bin/env python2.7
#
# b64_decode.py - must run with python 2.7
# - must process data in chunks to limit memory consumption
#
import os, sys, base64
def write_file_from_base64_file(b64_fname, dst_fname, chunk_size=8192):
with open(b64_fname, 'r') as fin, open(dst_fname, 'wb') as fout:
while True:
b64_data = fin.read(chunk_size)
if not b64_data:
break
print 'b64 %s data len: %d' % (type(b64_data), len(b64_data))
bin_data = base64.b64decode(b64_data)
print 'bin %s data len: %d' % (type(bin_data), len(bin_data))
fout.write(bin_data)
if len(sys.argv) != 2:
print 'usage: %s <b64_fname>' % sys.argv[0]
sys.exit()
b64_fname = sys.argv[1]
bin_ext = os.path.splitext(os.path.splitext(b64_fname)[0])[1]
bin_fname = os.path.splitext(b64_fname)[0] + bin_ext
write_file_from_base64_file(b64_fname, bin_fname)
举个例子,我对一个19k的图片文件的输出是:
$ ./b64_encode.py img.jpg
bin <type 'str'> data len: 8192
b64 <type 'str'> data len: 10924
bin <type 'str'> data len: 8192
b64 <type 'str'> data len: 10924
bin <type 'str'> data len: 2842
b64 <type 'str'> data len: 3792
$ ./b64_decode.py img.jpg.b64
b64 <type 'str'> data len: 8192
bin <type 'str'> data len: 6144
b64 <type 'str'> data len: 8192
bin <type 'str'> data len: 2048
b64 <type 'str'> data len: 8192
bin <type 'str'> data len: 4097
b64 <type 'str'> data len: 1064
bin <type 'str'> data len: 796
$ ll
19226 Feb 5 14:24 img.jpg
25640 Mar 29 12:12 img.jpg.b64
13085 Mar 29 12:14 img.jpg.jpg
1 个回答
4
你确实会遇到填充问题:
>>> open('pianoavatar.jpg').read(8192).encode('base64')[-5:]
'IIE=\n'
当Base64解码遇到=
这个填充标记时,就会停止解码。你的第二次读取在第10924个字符处找到了这样的标记。
为了避免在输出文件中间出现填充问题,你需要调整你的数据块大小,使其能被3整除。比如可以使用8190这个大小。
在读取时,你需要使用一个能被4整除的缓冲区大小,以避免对齐问题。8192
这个大小就可以,但你必须确保在你的函数中满足这个要求。最好是默认使用Base64扩展的数据块大小;对于8190的编码数据块大小,输入块的大小应该是10920(每3个字节编码成4个Base64字符)。
演示:
>>> write_base64_file_from_file('pianoavatar.jpg', 'test.b64', 8190)
bin <type 'str'> data len: 8190
b64 <type 'str'> data len: 10920
bin <type 'str'> data len: 8190
b64 <type 'str'> data len: 10920
bin <type 'str'> data len: 1976
b64 <type 'str'> data len: 2636
现在读取工作得很好,即使是你最初的8192数据块大小:
>>> write_file_from_base64_file('test.b64', 'test.jpg', 8192)
b64 <type 'str'> data len: 8192
bin <type 'str'> data len: 6144
b64 <type 'str'> data len: 8192
bin <type 'str'> data len: 6144
b64 <type 'str'> data len: 8092
bin <type 'str'> data len: 6068
你可以通过简单的取模运算来强制在你的函数中对齐缓冲区大小:
def write_base64_file_from_file(src_fname, b64_fname, chunk_size=8190):
chunk_size -= chunk_size % 3 # align to multiples of 3
# ...
def write_file_from_base64_file(b64_fname, dst_fname, chunk_size=10920):
chunk_size -= chunk_size % 4 # align to multiples of 4
# ...