Python一些可能用的到的函数系列101 图片base64的IO处理汇总

本文详细介绍了图片在计算机中的三种存在形态:文件形态、字符串形态和矩阵形态。讲解了如何通过Base64编码进行图片的传输,以及在服务端如何接收并处理这些数据。强调了在处理过程中内容的序列化、编码的重要性,同时提到了文件句柄的概念。最后,展示了如何在内存中以流形式处理图片数据,避免了频繁的磁盘读写操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明

已经写过一些相关的内容,现在完整的串一下。

内容

图片可以以文件形态、字符串形态和矩阵形态存在。

1 文件形态

对于计算机,都是二进制

2 读取一个图片并展示

文件形态下,可以使用特定的包(或者软件)代开一张图片并展示。这里,特定的包已经将二进制数据处理成了我们期望的展示方式。
在这里插入图片描述

当图片读入内存后,通常都是矩阵形态, 例如Image和numpy之间就可以方便的转来转去。

3 以二进制字符串方式读取一个图片

要进行传输时,我们通常要「序列化」。可以想象,字符串是序列化的,下面读取原始的字符串信息。

some_fpath = 'tem.png'
with open(some_fpath, 'rb') as f:
    file_bin_str = f.read()

在这里插入图片描述
可以看到,里面有一些文件的元信息。

4 进行编码

理论上,我们读到了文件的二进制字符串,就可以直接传送了,为什么要编码?主要的目的就是「不带误解」的传输。

由于某些系统中只能使用ASCII字符。Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法。Base64其实不是安全领域下的加密解密算法,而是一种编码,也就是说,它是可以被翻译回原来的样子。它并不是一种加密过程。所以base64只能算是一个编码算法,对数据内容进行编码来适合传输。虽然base64编码过后原文也变成不能看到的字符格式,但是这种方式很初级,很简单。

pic_str = base64.b64encode(file_bin_str)

在这里插入图片描述
看起来是整齐了,也没有特殊的字符。

为啥还要进行utf-8编码?我没有找到直接的答案,不过个人的理解是方便(从规范的角度)。

因为普通的接口请求都是以utf-8的方式传送字符串的,这样就和习惯对接。当然,其实是可以通过base64直接传送文件的(注意base64的encode还是二进制编码)。这里,二进制字符串要「翻译」给人看,所以叫decode,这个和base64的b64encode编码不是一回事。

pic_str1 = pic_str.decode('utf-8')

5 传送到服务端

很显然,如果将图片读取为字符串,显然是不是给人看的。

客户端使用request发送

import requests as req
input_dict = {'img_str':pic_str1}
test = req.post(host_ip, json = input_dict)

6 服务端接受

服务端(flask)直接这么解就可以了

input_data = request.get_json()

需要注意的点(一个踩过的坑):最好声明一下header字段里的content-type(默认的请求头是application/x-www-form-urlencoded), 对应的flask是request.get_data,这个是不一样的。如果这个搞错了, get_json是None, 而get_data是可以看到数据的。

先在服务端看一下刚才成功的提交请求头

print(request.headers)
Host: ...
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 359451
Content-Type: application/json

所以在客户端,一个标准一些的写法应该是

input_dict = {'img_str':pic_str1}
headers = {'Content-Type':"application/json; charset=utf-8"}
test = req.post(host_ip, json = input_dict, headers = headers)

7 服务端处理

以流的形式在内存中处理

服务端收到数据后,当然可以再存为图片。但如果要处理的话又得从硬盘读回来,而很多数据可能就处理一下(也不必保存),所以一般使用流的方式。

如果只是短时间的重复利用,并不希望长期持久化,而且对速度的要求比较高,这时候就可以考虑缓存。说到缓存,很多朋友就想到redis,熟悉python的朋友还会想到装饰器和闭包函数。
不过python已经原生为我们准备好了类文件对象(file-like object),这种对象在内存中创建,可以像文件一样被操作。

(上面那段话具体可参考这篇文章)

from io import BytesIO
b64_bin_str = input_dict['img_str'].encode('utf-8')
bin_str = base64.b64decode(b64_bin_str)
img_handler = BytesIO(bin_str)
img_in_memory = Image.open(img_handler)
img_in_memory

在这里插入图片描述
结果和我们直接打开一个本地文件是相同的。流程为:

  • 1 将图片字符串按utf-8重新变为二进制字符串(base64)
  • 2 将b64二进制字符串反编码,还原客户端发过来的图片二进制文件源字符串
  • 3 将二进制字符串处理(相当于开辟一个内存文件这步相当于是读取某个路径的图片),并返回文件句柄(handler)
  • 4 使用对应的程序包处理文件句柄。

关于handler

为了便于理解文件句柄,举个例子。这个错误应该都碰到过:文件句柄申请太多导致异常 Too many open files。简单来说每个文件系统都有一个最大的允许打开文件的数量,系统称为句柄(handler)。通常情况下我们不会碰到(大约是过一段时间无用句柄会被系统关闭),但是当运行程序时就很关键。所以python甚至出现了with xxx as f: xxx的语法,这样当使用完毕后句柄会立即关闭。

8 结束

到这里就差不多了,剩下的如果要保存啥的都是反过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值