为了使用二维码传输文件,上一篇文章已经实现了获取信息存入二维码并打印。由于单个二维码存储的信息量是有限的,而且文件一般也比较大,所以必须把文件先进行拆分,拆分后一块一块信息存入多张二维码中,最后通过图像识别,把所有二维码信息准确读取后再重新组装成文件。接下来一步一步讨论如何用Python实现文件拆分、打印二维码、识别二维码、重组文件四个过程。
目录
一、把文件拆分成块
首先读取文件,然后进行拆分。由于传输的文件格式多样,为了可以打开不同的文件格式,采用二进制方式读取文件是最通用的,通过以下Python代码可以实现文件的二进制读取,并一次文件读取所有内容。
其中pic001.png是csdn图标的截图,如下图,大小为2.74KB。
主要代码如下,mode='rb'设置了文件以二进制模式打开,read()方法一次性读取所有内容,若文件太大需要分段读取以免爆内存。
import os
# 使用二进制模式读取pic001.png图片
_file = open("pic001.png", mode="rb")
# 一次性读取所有字节(若文件太大请分段读取避免爆内存)
_contents = _file.read()
接着是拆分文件,这里需要特别注意的是由于后续文件需要通过二维码传输,若直接使用原编码进行传输,编码不一定能使用明文查看,导致程序调试不方便,因此这里先把读取出来的文件数据转换成base64编码再进行拆分和传输,对base64有兴趣的可以阅读以下文章一篇文章彻底弄懂Base64编码原理https://blog.csdn.net/wo541075754/article/details/81734770
主要代码如下,contents_base64=base64.b64encode(_contents),将_contents转换为base64编码,split_file()函数按固定长度拆分base64格式的字节流,其中_base64_str为字节流,_len为拆分的固定长度,最后返回拆分的字节列表_base64_str_list。
import base64
# 转换为_base64格式方面传输数据
contents_base64 = base64.b64encode(_contents)
print("拆分前编码:"+contents_base64.decode('utf-8'))
# 调用拆分函数
_temp_list = split_file(contents_base64,128)
# 按固定长度拆分_base64格式的字节流,_base64_str为字节流,_len为拆分的固定长度
def split_file(_base64_str,_len):
_base64_str_list = [_base64_str[i:i + _len] for i in range(0, len(_base64_str), _len)]
return _base64_str_list
输出结果如下,我们看到了熟悉的 AASUVORK5CYII=结尾的数据和列表。
拆分前编码:
iVBORw0KGgoAAAANSUhEUgAAAGkAAAApCAYAAAA22gdWAAAA(省略.....)gezoj6RGbYQAAAAASUVORK5CYII=
拆分后字节列表: [b'iVBORw0KGgoAAAANSUhEUgAAAGkAAAApCAYAAAA22gdWAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAApNSURBVHhe7Znn',(省略.....)
b'xCG5mMOUJ++Psr+8SCQHKkx5wv4M+0uLRL7/J6Y8SX+uSfA/gezoj6RGbYQAAAAASUVORK5CYII=']
二、把字节列表打印成多个二维码图片
文件拆分成字节列表后,下一步就是把每个列表的值存入二维码并打印了,这里直接用上一篇文章的打印二维码代码就可以了,代码如下。
import os
import qrcode
# 根据拆分后的字节流列表_base64_str_list,在cache文件夹下生成对应的二维码图片
def print_qr(_base64_str_list):
# 删除原来cache文件夹内的文件
for _pic_file in os.listdir("cache"):
os.remove("cache/"+_pic_file)
# 是否存在cache这个文件夹
if not os.path.exists("cache"):
os.makedirs("cache")
# 循环生成二维码图片
for i in range(0,len(_base64_str_list)):
qr = qrcode.QRCode()
qr.add_data(_base64_str_list[i], 0)
img = qr.make_image()
img.save("cache/split_file_00"+str(i)+".png")
这里需要注意的是,每次生成二维码图片前需要先清空cache文件夹原有的内容,cache文件夹是专门用来存放生成的二维码图片的,如果不提前清空,可能会残留上一次生成二维码图片的数据,导致后续合成文件出错。删除文件夹内文件这里用到了os.listder("cache")函数,用于返回cache下的文件列表,然后使用os.remove("cache/"+_pic_file)删除文件夹下的文件。
删除干净以后,循环调用qr.add_data()添加二维码信息,qr.make_image()和img.save()保存二维码图片到cache文件夹下面。最后看到cache文件夹下面出现了29张二维码图片,命名规则为split_file_00x,如下图:
三、利用OpenCV识别二维码图片并重组文件信息
到目前为止文件已经拆分成29张二维码图片了,现在需要做的是重新识别二维码,并重组文件。这里利用OpenCV的库cv2和库pyzbar完成识别,代码如下。
import cv2
from pyzbar import pyzbar
# 识别拆分后二维码还原并验证
def verify_qr():
_temp_str = ""
for i in range(0, len(os.listdir("cache"))):
# 读取二维码图片
qrcode = cv2.imread("cache/split_file_00"+str(i)+".png")
# 解析二维码中的数据
data = pyzbar.decode(qrcode)
# 在数据中解析出二维码的data信息
_temp_str = _temp_str + data[0].data.decode('utf-8')
return _temp_str
这里需要注意的是 如果使用os.listder("cache")函数返回二维码文件列表,列表不一定按文件名顺序返回(还没搞懂),所以只能使"cache/split_file_00"+str(i)+".png"文件名自增循环一个一个读,避免读取顺序出错。读取结果如下:
拆分前编码:
iVBORw0KGgoAAAANSUhEUgAAAGkAAAApCAYAAAA22gdWAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAkkJ
(省略......)
3OY8iR+a9OJxCG5mMOUJ++Psr+8SCQHKkx5wv4M+0uLRL7/J6Y8SX+uSfA/gezoj6RGbYQAAAAASUVORK5CYII=
还原后编码:
iVBORw0KGgoAAAANSUhEUgAAAGkAAAApCAYAAAA22gdWAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAkkJ
(省略......)
3OY8iR+a9OJxCG5mMOUJ++Psr+8SCQHKkx5wv4M+0uLRL7/J6Y8SX+uSfA/gezoj6RGbYQAAAAASUVORK5CYII=
可以看到,拆分前和识别还原后结果是一致的,证明文件信息已经正确重组了。以下是完整的代码。
import base64
import os
import cv2
from pyzbar import pyzbar
import qrcode
# 按固定长度拆分_base64格式的字节流,_base64_str为字节流,_len为拆分的固定长度
def split_file(_base64_str,_len):
_base64_str_list = [_base64_str[i:i + _len] for i in range(0, len(_base64_str), _len)]
return _base64_str_list
# 根据拆分后的字节流列表_base64_str_list,在cache文件夹下生成对应的二维码图片
def print_qr(_base64_str_list):
# 删除原来cache文件夹内的文件
for _pic_file in os.listdir("cache"):
os.remove("cache/"+_pic_file)
# 是否存在cache这个文件夹
if not os.path.exists("cache"):
os.makedirs("cache")
# 循环生成二维码图片
for i in range(0,len(_base64_str_list)):
qr = qrcode.QRCode()
qr.add_data(_base64_str_list[i], 0)
img = qr.make_image()
img.save("cache/split_file_00"+str(i)+".png")
# 识别拆分后二维码还原并验证
def verify_qr():
_temp_str = ""
for i in range(0, len(os.listdir("cache"))):
# 读取二维码图片
qrcode = cv2.imread("cache/split_file_00"+str(i)+".png")
# 解析二维码中的数据
data = pyzbar.decode(qrcode)
# 在数据中解析出二维码的data信息
_temp_str = _temp_str + data[0].data.decode('utf-8')
return _temp_str
if __name__ == '__main__':
# 使用二进制模式读取pic001.png图片
_file = open("pic001.png", mode="rb")
# 一次性读取所有字节(若文件太大请分段读取避免爆内存)
_contents = _file.read()
# 转换为_base64格式方面传输数据
contents_base64 = base64.b64encode(_contents)
print("拆分前编码:"+contents_base64.decode('utf-8'))
# 调用拆分函数
_temp_list = split_file(contents_base64,128)
print("拆分后字节列表:",_temp_list)
# 调用二维码生成函数
print_qr(_temp_list)
# 逐个识别所有生成的二维码,重新组合数据,验证是否正确
print("还原后编码:"+verify_qr())