使用Python Mongoengine存储读取PDF 解决Heroku文件丢失问题
问题根因排查
- 报错
No such file or directory: b''的直接原因:你错误地把art.uploaded_content.read()返回的PDF字节内容,作为文件路径传给了open()方法,open()的第一个参数需要是本地文件路径,不能直接传文件字节。另外如果读取文件后没有重置指针,后续调用read()只会返回空字节串。 - 报错
ValueError: embedded null byte的常见原因:你生成的article_pdf_filename包含空字符或非法字符,不符合本地文件系统的命名规则。
正确的Mongoengine存储&读取PDF实现
存储逻辑修正
import uuid from werkzeug.utils import secure_filename # 存储PDF的路由逻辑 if request.files['uploaded-article']: article_pdf = request.files['uploaded-article'] # 生成安全的随机文件名,避免空字符/非法字符问题 original_name = secure_filename(article_pdf.filename) file_ext = original_name.rsplit('.', 1)[1].lower() if '.' in original_name else 'pdf' article_pdf_filename = f"{uuid.uuid4().hex}.{file_ext}" data = Article() # 直接把文件对象存入FileField,指定文件类型避免解析错误 data.uploaded_content.put(article_pdf, content_type='application/pdf') data.uploaded_content_name = article_pdf_filename data.save()
读取逻辑修正
import os articles = Article.objects() for art in articles: save_path = os.path.join(app.config['BLOG_FOLDER'], art.uploaded_content_name) if not os.path.isfile(save_path): # 读取FileField的字节内容直接写入本地文件 pdf_content = art.uploaded_content.read() with open(save_path, 'wb') as f: f.write(pdf_content) # 重置文件指针,避免后续读取返回空字节 art.uploaded_content.seek(0)
Heroku静态文件丢失的替代方案
Heroku的文件系统是临时存储,每次部署、dyno重启都会清空所有写入的非代码内置文件,除了存MongoDB,还有几个更适配生产场景的方案:
- 对象存储服务:将PDF、图片直接上传到阿里云OSS、腾讯云COS等对象存储服务,仅把文件的CDN访问链接存在数据库中,无需本地存储,访问速度更快,也不受Heroku临时文件系统限制。
- 数据库直连访问:不需要把PDF读入本地静态文件夹,直接做一个路由接口,用户请求文件时实时从MongoDB读取字节返回给前端,完全跳过本地存储步骤。
- 静态资源打包部署:如果是更新频率极低的固定文件,直接把资源存入代码仓库,和代码一起打包部署,就不会被临时文件系统清空。
内容的提问来源于stack exchange,提问作者Harley
相关产品推荐
相关产品推荐

