使用psycopg2将Telegram用户照片存入PostgreSQL失败的问题求助
Telegram机器人存图片到PostgreSQL的语法错误解决办法
问题出在你写的insert函数里——直接用字符串格式化把参数拼进SQL语句了。当你传入二进制图片数据时,Python会把字节对象或者psycopg2.Binary对象转成它的默认字符串形式(比如<psycopg2._psycopg.Binary object at 0x...>或者一堆乱码的字节串),PostgreSQL根本认不出这是什么,自然报语法错误,而且这么写还会有SQL注入的风险,绝对不能这么干。
正确做法:用参数化查询
把insert函数改成下面这样,用%s当占位符,把参数单独传给execute方法,psycopg2会自动帮你处理二进制数据的格式:
def insert(table: str, column_values: Dict) -> None: columns = ', '.join(column_values.keys()) # 给每个参数生成一个%s占位符 placeholders = ', '.join(['%s'] * len(column_values)) q = f'''INSERT INTO {table} ({columns}) VALUES ({placeholders})''' # 把参数作为第二个参数传给execute,不要拼进SQL里 cursor.execute(q, tuple(column_values.values())) connection.commit()
配合两种图片获取方式的正确用法
不管你用哪种方式拿图片数据,只要传给column_values的是字节数据或者psycopg2.Binary包装的数据,参数化查询都能正确处理:
方式1:用urllib拉取图片
def parse_photo(file_path: str): import urllib.request import os import psycopg2 with urllib.request.urlopen(f"https://api.telegram.org/file/bot{os.getenv('TOKEN')}/{file_path}") as f: image_data = f.read() # 可以用psycopg2.Binary包装,也可以直接传字节数据,两种都可以 return psycopg2.Binary(image_data)
调用示例:
# 假设已经拿到file_path data = { "user_id": 297850814, "some_field": "sdf", "date": "09.04.2023", "num": 7, "photo": parse_photo(file_path) } insert("你的表名", data)
方式2:用bot.download_file拉取图片
# 异步环境下的代码 file_info = await bot.get_file(message.photo[-1].file_id) # 直接读取字节数据 image_data = (await bot.download_file(file_info.file_path)).read() data = { "user_id": 297850814, "some_field": "sdf", "date": "09.04.2023", "num": 7, "photo": image_data # 直接传字节就行,不用额外包装 } # 如果insert是同步函数,异步环境下要放到线程池执行,避免阻塞事件循环 import asyncio loop = asyncio.get_event_loop() await loop.run_in_executor(None, insert, "你的表名", data)
额外提醒
- 数据库里的
photo字段类型必须是BYTEA,这是PostgreSQL专门存二进制数据的类型,别建错了。 - 以后写SQL操作都要用参数化查询,别再直接拼字符串了,既安全又能避免各种奇奇怪怪的语法问题。
内容的提问来源于stack exchange,提问作者pizhlo
相关产品推荐
相关产品推荐

