Heroku部署Django项目时找不到pdflatex生成的PDF文件
问题根因
报错的核心原因是pdflatex进程根本没在指定路径生成texput.pdf,本地能跑通但线上失败的差异来自Heroku平台的固有特性,和你列的三个猜测方向完全对应:
- Heroku基础运行镜像默认不带LaTeX发行版,本地能跑是因为本地安装了TeX环境,线上直接调用
pdflatex会触发命令不存在的错误,你的代码没有捕获进程返回值和错误流,完全感知不到执行失败。 - Heroku dyno的文件系统是临时分层结构,除
/tmp目录外,其余项目目录在运行时是只读的,往templates、media这类目录写文件会直接触发权限错误;就算写入成功,dyno重启、重新部署、扩缩容时所有本地文件都会被清空,且多dyno实例之间文件完全不共享。另外你通过heroku run bash登录的是全新的一次性dyno,和处理请求的业务dyno文件系统完全隔离,自然找不到业务进程生成的文件。 - 你调用
pdflatex时没有指定固定任务名,本地默认生成texput.pdf不代表线上环境默认输出文件名一致,工作目录、环境变量差异都可能导致输出文件名/路径偏移。
修复&排查步骤
1. 先补全进程错误捕获,定位具体失败点
不要静默执行subprocess命令,把标准输出、错误流、返回码都打在日志里,一眼就能看到失败原因:
import os import subprocess from subprocess import PIPE from pathlib import Path def createPDF(name): # 临时文件统一用/tmp目录,这是Heroku运行时唯一可写的临时路径 tmp_dir = '/tmp' template = get_template('src.tex') context = {'content': name} rendered_tpl = template.render(context).encode('utf-8') process = subprocess.Popen( # 加-jobname固定输出文件名,强制指定输出目录到/tmp ['pdflatex', '-output-directory', tmp_dir, '-jobname', 'cv_output'], stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=tmp_dir ) # 加超时避免进程卡死 stdout, stderr = process.communicate(rendered_tpl, timeout=30) # 打印调试信息,直接看Heroku日志即可 print(f"pdflatex执行返回码: {process.returncode}") print(f"pdflatex标准输出: {stdout.decode('utf-8', errors='ignore')}") print(f"pdflatex错误输出: {stderr.decode('utf-8', errors='ignore')}") # 遍历/tmp下的pdf文件,确认生成结果 for f in os.listdir(tmp_dir): if f.endswith('.pdf'): pdf_path = os.path.join(tmp_dir, f) print(f"找到生成的PDF文件: {f}, 文件大小: {os.path.getsize(pdf_path)}字节")
部署后触发一次函数,看Heroku日志:如果返回码是127,就是环境里没有pdflatex;如果是权限错误,就是之前写的目录不可写。
2. 补全线上LaTeX运行环境
Heroku基础镜像不带pdflatex,需要在应用中添加TeX环境对应的buildpack,确保运行时可以正常调用pdflatex命令。
3. 修正文件存储逻辑
绝对不要在Heroku本地磁盘存持久化文件:
- 临时生成的PDF全部写到
/tmp目录,不要往项目内置的templates、media目录写任何运行时生成的内容。 - 如果需要持久保存PDF,生成后立刻把文件上传到外部对象存储服务,不要存在本地磁盘。
- 如果只是需要把PDF返回给用户下载,直接读取
/tmp下生成的PDF字节流返回HttpResponse即可,不用落盘到项目目录。
对你列出的三个猜测的明确答复
- 关于PDF未创建:90%以上概率是这个原因,要么pdflatex命令不存在,要么目录无写入权限,用上面的错误捕获代码可以直接定位具体错误。
- 关于文件名不符合预期:调用pdflatex时加上
-jobname 固定名称参数,就能强制输出文件名为固定名称.pdf,不需要依赖默认命名规则。 - 关于文件存储路径偏移:强制指定
-output-directory为/tmp,同时设置进程工作目录为/tmp,就不会出现路径错乱的问题;不要通过bash找文件,因为登录的bash是独立dyno,和业务进程的文件系统不互通,直接看日志打印的文件遍历结果即可。
内容的提问来源于stack exchange,提问作者GCMeccariello
相关产品推荐
相关产品推荐

