Django/ReportLab:将生成的PDF直接保存到AWS S3中的FileField。

4

我正在编写一个Django应用程序,需要使用reportlab生成PDF并将其保存到FileField中。 我的问题是我的媒体存储在S3中,我不知道如何直接将此pdf保存到S3。

我会先在本地生成pdf,然后读取并保存到s3中的FileField,最后删除本地版本。但我想知道是否有更好的解决方案,可以直接将文件保存到S3。

现在是我生成合同的方式:

#temporary folder name to generate contract locally
folder_name = (
     '/tmp/'
     + '/contracts/'
     + gig.identifier
     )
mkdir_p(folder_name)
address = (
     folder_name
     + '/contract_'
     + lang
     + '.pdf'
 )
doc = SimpleDocTemplate(
     address,
     pagesize=A4,
     rightMargin=72,
     leftMargin=72,
     topMargin=120,
     bottomMargin=18
 )
doc.build(Story,
          canvasmaker=LogoCanvas)
local_file = open(address)
pdf_file = File(local_file)
contract = Contract.objects.create(lang=lang, gig=gig)
contract.contract_file.save('contract_'+lang+'.pdf', pdf_file)
contract.save()
#remove temporary folder
shutil.rmtree(folder_name)
return True

合同模型的定义如下:

class Contract(models.Model):
    gig = models.ForeignKey(Gig, related_name='contracts')
    lang = models.CharField(max_length=10, default='')
    contract_file = models.FileField(
        upload_to=get_contract_path,
        blank=True,
        null=True,
        default=None
    )

你是如何解决这个问题的? - Diego Gallegos
@DiegoGallegos 我不认为我这样做了。如果我没记错的话,我一直在本地编写PDF,然后保存它。 - dietbacon
3个回答

2
您可以直接将生成的PDF文件保存到S3中。
我使用boto3库来处理在S3中的存储,我认为您也是使用相同的方法。
from api.models import Entity
from django.core.files.base import ContentFile

entity = Entity.objects.create(**create_params)
pdf = render_to_pdf('invoice.html') # returns BytesIO
entity.pdf_field.save('invoice.pdf', ContentFile(pdf.getvalue()), save=True) 

1
您可以使用 BytesIO 创建一个内存流,而不是文件流。
但是,您仍然需要使 reportlab 使用该流,而不是文件。
userguide 中了解更多信息,在 2.2 More about the Canvas 中,Canvas 构造函数有一个 filename 参数,可同时是字符串或文件描述符。我已经使用 SimpleDocTemplate 进行了测试,所以它应该适用于您:
import io
stream = io.BytesIO()
doc = SimpleDocTemplate(
     stream,
     pagesize=A4,
     rightMargin=72,
     leftMargin=72,
     topMargin=120,
     bottomMargin=18
 )
doc.build(Story,
          canvasmaker=LogoCanvas)
pdf_buffer = stream.getBuffer()

我不了解S3,但我相信这已经足以解决您的问题。例如,如果您现在想将此PDF写入文件,您可以执行以下操作:
file = open("contract.pdf", "wb")
file.write(pdf_buffer)

1
我将 stream.getBuffer() 替换为 stream.getvalue(),这样就更容易将数据写入远程服务器了。 - Jakob

0
你可以使用标准的Python模块tempfileimport tempfile
tempfile.TemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix=None, prefix=None, dir=None)

返回一个类似文件的对象,可用作临时存储区。该文件是安全创建的,使用与mkstemp()相同的规则。它将在关闭时被销毁(包括在对象进行垃圾回收时的隐式关闭)。在Unix下,文件的目录条目要么根本不创建,要么在文件创建后立即删除。其他平台不支持此功能;您的代码不应依赖于使用此函数创建的临时文件是否在文件系统中具有可见名称。 https://docs.python.org/3/library/tempfile.html 顺便说一句,你欠我25美元 =)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接