Base64是网络上最常见的用于传输8位字节码的编码方式之一,其原理是基于64个可打印字符来表示二进制数据。
本文将讲述如何使用Django Rest framework框架,处理Base64格式传输的图片文件。
01
Base64介绍
当我们尝试用记事本打开exe、jpg、pdf这些文件时,会看到一堆又一堆的乱码。
这是因为,exe、jpg、pdf这些文件属于二进制文件,文件中是包含很多无法显示和打印的字符的,当我们用记事本打开这些二进制文件时,就需要一个二进制到字符串的转换方法。
Base64就是一种常见的二进制编码方法。
02
Base64原理简介
Base64的原理很简单,首先,准备一个包含64个字符的字符库。
然后,在字符库的基础上对二进制数据进行处理,处理规则如下:每3个字节一组,一共是`3x8=24`bit,划为4组,每组正好6个bit。
这样我们得到4个数字作为每组的索引值,根据索引进行查表,就可以获得表中相应的4个字符,就是编码后的字符串。
所以,使用Base64编码会把3字节的二进制数据编码为4字节的文本数据,虽然数据的长度增加33%,但是可以作为文本数据在邮件正文、网页等直接显示。
03
Python中的base64模块
Python中直接内置了base64模块,可以非常方便直接的进行base64的编解码操作:
我们可以看下示例代码:
# 导入base64模块import base64# 创建一个bytes数据byte_data = b'hello world'# 使用base64模块中的b64encode方法进行编码base64_byte = base64.b64encode(byte_data)# 打印编码后的数据print(base64_byte) # b'aGVsbG8gd29ybGQ='# 使用base64模块中的b64decode方法进行解码print(base64.b64decode(base64_byte))
通过运行这段示例代码,我们可以看到 b'hello world' 被转换成base64字节码b'aGVsbG8gd29ybGQ='
除此之外,base64模块中还提供了urlsafe_b64encode()和urlsafe_b64decode()方法,这些方法是用于生成不包含+和/的字符的字节码的。
因为在URL查询字符串中不支持+号与/,所以urlsafe_b64encode()与urlsafe_b64decode()中会把字符+和/分别变成-和_从而来匹配URL
04
DRF中的base64解析
我们现在来设想一种应用场景,前端将一张图片文件转换成Base64字节码,通过API传递给后端,后端要正常保存该图片的话,是需要将前端传递过来的Base64字节码解码成文件再保存的。
在Django Rest framework (下文简称为DRF)可以这样进行处理。
1. 通过继承ImageField类,自定义一个Base64ImageField类,专门用于对Base64格式的图片文件进行映射
2. 在Base64ImageField类中,重写to_internal_value方法。
代码如下:
def to_internal_value(self, data): from django.core.files.base import ContentFile import base64 import six import uuid # 检测字段中的内容是否为base64字节码 if isinstance(data, six.string_types): # 是否包含 data 和 ;base64 if 'data:' in data and ';base64,' in data: # Break out the header from the base64 content header, data = data.split(';base64,') # 对文件进行解码 try: decoded_file = base64.b64decode(data) except TypeError: self.fail('invalid_image') # 生成随机文件名: file_name = str(uuid.uuid4())[:12] # 12个字符长度的uuid字符串 # 获取文件后缀名: file_extension = self.get_file_extension(file_name, decoded_file) complete_file_name = "%s.%s" % (file_name, file_extension, ) data = ContentFile(decoded_file, name=complete_file_name) return super(Base64ImageField, self).to_internal_value(data)
to_internal_value方法,用于将前端上传的文件转换成Django框架中的文件对象类型ContentFile,从而能够正常执行框架中的数据保存操作。
我们在重写该方法时,检测下前端上传数据是否符合格式,然后使用Base64模块进行解码即可。
3. 在重写to_internal_value方法时,为了获取前端上传图片的后缀名(文件格式),可以引入python内置的imghdr模块。
imghdr 模块可以推测文件或字节流中的图像的类型,从而获取文件后缀名。
我们将检测图片后缀名的相关代码封装成一个实例方法:get_file_extension,该方法会检测输入文件的类型,并返回输入文件的后缀名。
# 获取文件后缀名的方法def get_file_extension(self, file_name, decoded_file): # 引入imghdr模块用于检测图片的类型 import imghdr extension = imghdr.what(file_name, decoded_file) extension = "jpg" if extension == "jpeg" else extension return extension
在实际的开发过程中,我们只需要在对应的序列器Serializer中针对上传的图片项使用Base64ImageField这样一个Field类即可。
当数据保存时,对应的序列器会调用save()方法,会依次执行序列器中各个field中的to_internal_value方法,从而实现图片数据的类型转换并保存。
05
完整示例代码
from rest_framework import serializersclass Base64ImageField(serializers.ImageField): def to_internal_value(self, data): from django.core.files.base import ContentFile import base64 import six import uuid # 检测字段中的内容是否为base64字节码 if isinstance(data, six.string_types): # 是否包含 data 和 ;base64 if 'data:' in data and ';base64,' in data: # Break out the header from the base64 content header, data = data.split(';base64,') # 对文件进行解码 try: decoded_file = base64.b64decode(data) except TypeError: self.fail('invalid_image') # 生成随机文件名: file_name = str(uuid.uuid4())[:12] # 12个字符长度的uuid字符串 # 获取文件后缀名: file_extension = self.get_file_extension(file_name, decoded_file) complete_file_name = "%s.%s" % (file_name, file_extension, ) data = ContentFile(decoded_file, name=complete_file_name) return super(Base64ImageField, self).to_internal_value(data) # 获取文件后缀名的方法 def get_file_extension(self, file_name, decoded_file): # 引入imghdr模块用于检测图片的类型 import imghdr extension = imghdr.what(file_name, decoded_file) extension = "jpg" if extension == "jpeg" else extension return extension
![f1b7fda287551a850cec5508acea3daa.gif](https://i-blog.csdnimg.cn/blog_migrate/ac01bae76dcf597b9ea28d6833ba5637.gif)
END