Flask中嵌入MongoDB图片到PDF遇报错,求解决方案
问题解决与优化方案
一、报错原因及修复
报错AttributeError: 'ImageReader' object has no attribute 'read'是因为你错误地对ImageReader实例调用了read()方法——ImageReader本身并不提供read()接口,它是用于读取图片内容的对象,而非字节流容器。
修复当前报错的代码片段
替换原代码中从remote_file = ImageReader(urlopen(img_url)).read()到return send_file(...)的部分:
from PIL import Image import io # 获取图片字节流 img_bytes = urlopen(img_url).read() memory_file = io.BytesIO(img_bytes) # 重新初始化画布并绘制内容 buf = io.BytesIO() c = canvas.Canvas(buf, pagesize=letter) c.setFont("Helvetica", 20) c.drawString(100, 720, dog_name) # 直接在画布上绘制图片,可调整位置和尺寸 c.drawImage(memory_file, 100, 500, width=200, height=200) c.save() # 必须调用save完成画布内容写入 buf.seek(0) return send_file(buf, mimetype='application/pdf')
二、更优的PDF图片嵌入方案
你的现有代码存在冗余步骤(创建两个PDF再合并),更高效的方式是直接在ReportLab Canvas上绘制所有元素(文字+图片),避免不必要的PDF合并操作,同时提升代码可读性和性能。
完整优化后的代码
from flask import send_file import pymongo from bson import json_util import io from reportlab.lib.pagesizes import letter from reportlab.pdfgen import canvas from PIL import Image from urllib.request import urlopen def get(self, usermail, dog_name): # 连接MongoDB并查询数据 client = pymongo.MongoClient('mongodb://uri') filter_query = {'UserMail': usermail, 'title': dog_name} # 直接提取单条结果(假设查询唯一匹配) dog_data = list(client['db']['Todo'].find(filter=filter_query))[0] # 提取宠物信息 img_url = 'https://url' + dog_data['DogImg'] dog_age = dog_data['Age'] dog_desc = dog_data['DogDescription'] dog_name = dog_data['title'] dog_breed = dog_data['Breed'] # 创建PDF画布 buf = io.BytesIO() c = canvas.Canvas(buf, pagesize=letter) page_width, page_height = letter # 绘制居中标题 c.setFont("Helvetica-Bold", 20) title_x = page_width/2 - len(dog_name)*6 # 粗略计算居中位置 c.drawString(title_x, page_height - 50, dog_name) # 绘制自适应尺寸的图片 try: img_bytes = urlopen(img_url).read() img_stream = io.BytesIO(img_bytes) img = Image.open(img_stream) # 计算缩放比例,避免图片超出页面 max_width = page_width - 100 max_height = page_height - 200 scale = min(max_width / img.width, max_height / img.height) draw_width, draw_height = img.width*scale, img.height*scale # 居中绘制图片 c.drawImage(img_stream, (page_width-draw_width)/2, page_height - 150 - draw_height, width=draw_width, height=draw_height) except Exception: # 图片加载失败时绘制提示 c.setFont("Helvetica", 12) c.drawString(page_width/2 - 50, page_height - 200, "图片加载失败") # 绘制宠物详情信息 c.setFont("Helvetica", 14) info_y = page_height - 200 - draw_height - 30 c.drawString(50, info_y, f"品种: {dog_breed}") c.drawString(50, info_y - 30, f"年龄: {dog_age}") c.drawString(50, info_y - 60, f"描述: {dog_desc}") # 保存画布并返回PDF c.save() buf.seek(0) return send_file(buf, mimetype='application/pdf', download_name=f"{dog_name}_信息.pdf")
优化点说明
- 简化数据处理:省去JSON序列化/反序列化、DataFrame转换的冗余步骤,直接从MongoDB结果中提取数据,提升性能。
- 直接绘制元素:在同一个画布上完成文字和图片绘制,避免多PDF合并的额外开销。
- 自适应布局:自动计算图片缩放比例,确保内容适配页面,同时保持居中显示。
- 错误处理:添加图片加载失败的异常捕获,提升代码健壮性。
- 用户体验优化:设置自定义下载文件名,方便用户识别文件。
内容的提问来源于stack exchange,提问作者Lash
相关产品推荐
相关产品推荐

