Django中如何将DO Spaces存储的图片插入ReportLab生成的PDF
问题:ReportLab无法加载DigitalOcean Spaces中的SVG图片
问题场景
我尝试将存在DigitalOcean Spaces里的SVG logo插入到ReportLab生成的PDF中,写的代码是:
pdf.drawImage('https://dummy-bucket.sgp1.digitaloceanspaces.com/static/images/logo_name_header.svg', -0.10*cm,10*cm)
但运行时直接报错:
fileName=<__io.BytesIO object at 0x000001D1CE603A90> identity=[ImageReader@0x1d1ce5e6f70] cannot identify image file <_io.BytesIO object at 0x000001D1CE603A90>
图片用浏览器能正常打开,完整代码如下:
def get_pdf(request): data= { ... some data } try: buffer = io.BytesIO() pdf = canvas.Canvas(buffer, pagesize=A4) pdf = base_temp(pdf, data) #base temp is a pre designed canvas pdf.setTitle("Invoice") pdf.drawImage('https://dummy-bucket.sgp1.digitaloceanspaces.com/static/images/logo_name_header.svg', -0.10*cm,10*cm) pdf.showPage() pdf.save() buffer.seek(0) # sending pdf as a response response = FileResponse(buffer) # setting the content-type to pdf response['Content-Type'] = 'application/pdf' # telling the response that it is a pdf file response['Content-Disposition'] = f'inline; filename="{data["invoice_number"]}.pdf"' return response except Exception as e: print(e) return HttpResponse(str(e))
原因
ReportLab的drawImage本身不支持SVG格式,它只认PNG、JPG这类光栅图。另外就算是支持的格式,直接传远程URL也可能因为底层读取逻辑的问题加载失败,更别说不兼容的SVG了。
解决方案
有两种靠谱的解决办法:
办法1:把SVG转成PNG/JPG再加载
先把SVG下载到内存里,转成ReportLab支持的格式,再插入PDF。用cairosvg就能完成转换:
- 先装依赖:
pip install cairosvg requests
- 修改代码:
import requests import cairosvg from io import BytesIO def get_pdf(request): data = { # ... 你的业务数据 } try: buffer = io.BytesIO() pdf = canvas.Canvas(buffer, pagesize=A4) pdf = base_temp(pdf, data) pdf.setTitle("Invoice") # 1. 下载SVG内容 svg_url = 'https://dummy-bucket.sgp1.digitaloceanspaces.com/static/images/logo_name_header.svg' svg_resp = requests.get(svg_url) svg_resp.raise_for_status() # 确保请求没出错 # 2. 把SVG转成PNG存在内存里 png_buffer = BytesIO() cairosvg.svg2png(bytestring=svg_resp.content, write_to=png_buffer) png_buffer.seek(0) # 把指针移到文件开头 # 3. 插入转好的PNG到PDF pdf.drawImage(png_buffer, -0.10*cm, 10*cm) pdf.showPage() pdf.save() buffer.seek(0) response = FileResponse(buffer) response['Content-Type'] = 'application/pdf' response['Content-Disposition'] = f'inline; filename="{data["invoice_number"]}.pdf"' return response except Exception as e: print(e) return HttpResponse(str(e))
办法2:用ReportLab的SVG扩展
ReportLab有个rl_svg扩展可以直接处理SVG,步骤更简单:
- 装依赖:
pip install reportlab-svg
- 修改代码:
from rl_svg import draw_svg def get_pdf(request): data = { # ... 你的业务数据 } try: buffer = io.BytesIO() pdf = canvas.Canvas(buffer, pagesize=A4) pdf = base_temp(pdf, data) pdf.setTitle("Invoice") # 用rl_svg直接加载并绘制SVG svg_url = 'https://dummy-bucket.sgp1.digitaloceanspaces.com/static/images/logo_name_header.svg' draw_svg(pdf, svg_url, x=-0.10*cm, y=10*cm) pdf.showPage() pdf.save() buffer.seek(0) response = FileResponse(buffer) response['Content-Type'] = 'application/pdf' response['Content-Disposition'] = f'inline; filename="{data["invoice_number"]}.pdf"' return response except Exception as e: print(e) return HttpResponse(str(e))
额外提醒
- 如果用
cairosvg,服务器环境需要装系统依赖libcairo2,Ubuntu下可以用apt install libcairo2安装 - 如果你的DigitalOcean Spaces需要权限访问,要在请求里加对应的认证信息(Spaces兼容S3 API,用AWS签名逻辑就行)
- 可以加个缓存,别每次生成PDF都重新下载转换SVG,能提升性能
内容的提问来源于stack exchange,提问作者Ritankar Bhattacharjee
相关产品推荐
相关产品推荐

