Flask读取PostgreSQL中bytea类型PDF下载报错问题排查
问题原因
下载得到的PDF空白、格式错误,本质是三个常见问题:
- psycopg2默认读取PostgreSQL的
bytea类型字段时,返回的是数据库hex编码后的字节序列(默认以\x作为前缀,不是PDF的原始二进制流)。比如原始PDF开头的%PDF-格式标识会被编码为\x255044462d,直接把这个编码后的内容写入文件,PDF阅读器无法识别格式,自然显示空白或损坏。 - 调用
send_file时未指定application/pdf的MIME类型,部分浏览器/客户端会按默认文本类型处理响应,额外注入字符或修改内容,进一步损坏文件。 - 现有代码未释放数据库游标、连接资源,高并发场景下会触发数据库连接泄漏。
快速验证方式:把下载的损坏PDF用文本编辑器打开,如果文件开头是
\x拼接的十六进制字符串,就可以确认是bytea编码未解码的问题。
修复代码
先在数据库连接层面配置bytea输出格式,同时兼容处理可能存在的hex编码前缀,指定正确的响应MIME类型,补全资源释放逻辑:
from flask import send_file from io import BytesIO import psycopg2 import psycopg2.extras @app.route('/download_file') def downloadFile(): conn = None cur = None try: # 替换为你实际的数据库连接参数 conn = psycopg2.connect( host="你的数据库地址", dbname="你的库名", user="你的账号", password="你的密码" ) cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) # 配置数据库直接返回原始bytea二进制,不做hex转义 cur.execute("SET bytea_output = 'escape';") # 实际业务可从请求参数动态获取fail_id,例:fail_id = request.args.get("fail_id", type=int) fail_id = 9 # 用参数化查询,避免SQL注入和二进制转义问题 cur.execute("SELECT email_pdf FROM fails WHERE fail_id = %s", (fail_id,)) row = cur.fetchone() if not row or not row["email_pdf"]: return "指定文件不存在", 404 pdf_content = row["email_pdf"] # 兼容兜底:如果返回内容带\x的hex前缀,手动解码为原始二进制 if pdf_content.startswith(b'\\x'): pdf_content = bytes.fromhex(pdf_content[2:].decode("ascii")) return send_file( BytesIO(pdf_content), mimetype="application/pdf", as_attachment=True, download_name="new.pdf" ) except Exception as e: return f"文件下载失败:{str(e)}", 500 finally: # 必须释放数据库资源 if cur: cur.close() if conn: conn.close()
额外排查项
如果按上述代码修改后文件仍然损坏,需要检查PDF写入数据库的逻辑:
- 写入
bytea字段时,必须传入PDF文件的原始二进制bytes对象,不能将二进制内容做utf-8/gbk等文本解码后再存储,否则会不可逆破坏PDF格式 - 写入时必须使用psycopg2的参数化查询传值,不要手动拼接SQL字符串,避免特殊字符被转义损坏
内容的提问来源于stack exchange,提问作者hekuma
相关产品推荐
相关产品推荐

