【问题标题】:Django/reportlab: Save generated pdf directly to FileField in AWS s3Django/reportlab:将生成的 pdf 直接保存到 AWS s3 中的 FileField
【发布时间】:2017-04-12 14:52:23
【问题描述】:

我正在编写一个 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
    )

【问题讨论】:

  • 你是怎么解决这个问题的?
  • @DiegoGallegos 我想我没有。我一直在本地编写 PDF,然后如果我没记错就保存它。

标签: python django amazon-web-services amazon-s3 reportlab


【解决方案1】:

您可以将生成的 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) 

【讨论】:

    【解决方案2】:

    您可以使用BytesIO 创建内存流而不是文件流。

    但是,您仍然需要让 reportlab 使用该流而不是文件。

    依靠userguide,在2.2 更多关于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)
    

    【讨论】:

    • 对我来说,我用stream.getvalue() 替换了stream.getBuffer(),它能够更轻松地写入远程服务器。
    【解决方案3】:

    您可以使用标准 Python 模块 tempfile (import 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 您对 tempfile 对象的使用与普通文件完全相似。

    顺便说一句,你欠我 25 美元 =)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-15
      • 2011-05-21
      • 1970-01-01
      • 2016-03-19
      相关资源
      最近更新 更多