[难点攻破] python requests上传微信公众临时素材

项目目标

用户上传一个图片,接收到之后进行数组操作,然后返回处理好的图片,要求全部在内存中完成

项目分析

微信本身给的上传接口的curl写的命令

curl -F media=@{image_name} "https://api.weixin.qq.com/cgi-bin/media/upload?access_token={access_token}&type=image"

如果要在命令行里输入图片名字就必须经过文件系统,不能满足全内存操作的要求,所以必须curl命令转requests请求

https://curlconverter.com/

通过这个网站可以轻松把curl转化成requests,然后一看结果:

import requests

files = {
    'media': open('{image_name}', 'rb'),
}

response = requests.post('https://api.weixin.qq.com/cgi-bin/media/upload?access_token={access_token}&type=image', files=files)

然后我就笑了,这还是得经过文件系统,没办法,得自己做一个二进制流。

files = {'media': BytesIO(image_bytes)}
response = requests.post(url, files=files)    

这样是不行的,返回错误信息

{'errcode': 40005, 'errmsg': 'invalid file type hint: [vIQHea01094248] rid: 66193184-3f53f282-7178c05f'}

为了检测到底是curl转化requests的有问题,还是BytesIO有问题,我试了一下经过文件系统的例子,是可以的。于是想办法找区别,首先是open函数返回值是一个BufferedReader,我就想着包一层

f1 = BufferedReader(BytesIO(image_name, image_bytes), image_bytes.__sizeof__())

很明显这样也是不行的,返回值还是错误,我又print了一下两种句柄的区别

f1 = BytesIO(image_bytes)
f2 = open(f'{image_name}', 'rb')
print(f1, '\n', f2)

<_io.BufferedReader>
<_io.BufferedReader name='./tmp/6777248.png'>

哦,原来是少了name这个property,那我加上。

嘿,你猜怎么着,没法加,BytesIO的name的readonly,无法修改,那我不管,重新派生一个类。

class NamedBytesIO(io.BytesIO):
    def __init__(self, name, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = name

<_io.BufferedReader name='./tmp/4365267.png'>
<_io.BufferedReader name='./tmp/4365267.png'>

这下打印出来名字了,但还是报错啊,没上传成功,然后我就气得跳脚了。

我就想啊,为什么保存到文件系统里就行,内存里直接发比特流就不行?

有没有可能保存的那一下有情况?

image_name = './tmp/' + str(random.randint(1000000, 9999999)) + img_suffix
# cv2.imwrite(image_name, image)
image.save(image_name)

image.save是不是把图片给变化了?

猛然回想,看他报错是什么?

{'errcode': 40005, 'errmsg': 'invalid file type hint: [vIQHea01094248] rid: 66193184-3f53f282-7178c05f'}

文件不合法?难道我bytes不是图片形式?

然后我突然想到我处理图片的逻辑里,是先要把jpeg类型转化为向量类型,操作像素的的时候是np.ndarray,但是这玩意肯定不是jpeg类型,压缩算法之后的数据肯定不一样。

def cut_white_border(image_bytes: bytes):
    image_pil = Image.open(BytesIO(image_bytes))
    if image_pil.format == 'PNG':
        img_suffix = '.png'
    elif image_pil.format == 'JPG':
        img_suffix = '.jpg'
    else:
        return 'error'
    # image_mode = image.mode

    image_array = np.array(bytearray(image_bytes), dtype=np.uint8)
    image = cv2.imdecode(image_array, cv2.IMREAD_UNCHANGED)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

所以最后我还得把像素信息压缩回jpeg格式

yes, image_tobytes = cv2.imencode('.jpg', image.astype(np.uint8))

好了,解决了,花了我俩小时,我真是蠢啊。

{'type': 'image', 'media_id': '6Pym7GRK0gDsNikc6iUlsLcFD3I1cQuyEAAJZz9y6CUwh5-znSw7biPXbqOhLmEv', 'created_at': 1712927076, 'item': []}

最后解决的时候是这样的,要加上一个名字,否则还是会有问题,name这个property是有用的。

files = {'media': (image_name, BytesIO(image_bytes))}
response = requests.post(url, files=files)    

我没试自己派生的那个类是否可以通过,我感觉是可以的,不过这就是requests库内部的处理了,传入tuple可以是2,3,4三种情况,细节就请进去看注释吧。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值