【问题标题】:Saving a Pillow file to S3 with boto使用 boto 将 Pillow 文件保存到 S3
【发布时间】:2016-02-10 07:41:46
【问题描述】:

我通过 s3boto 使用 Amazon S3 作为我的存储后端。我有一个带有 ImageField 的图像模型。当通过管理员上传图像时,它已成功上传到 S3。我现在要做的是使用 Pillow 创建一个缩略图保存后。我已经通过调用它的 show() 方法验证了正在创建缩略图,但由于某种原因它没有被上传到 S3。我认为我保存它的方式可能是错误的 - 请提供任何建议。

tasks.py

from celery import shared_task
from .models import Image
import os
from django.core.files.storage import default_storage as storage
from PIL import Image as PillowImage

@shared_task
def create_thumbnails(pk):
    try:
        image = Image.objects.get(pk=pk)
    except Image.ObjectDoesNotExist:
        pass
    thumbnail_size = (450,200)
    filename, ext = os.path.splitext(image.image.name)
    try:
        fh = storage.open(image.image.name, 'r')
        im = PillowImage.open(fh)
        im.thumbnail(thumbnail_size)
        im.show() # TEST - This opens the resized image in Preview on my mac
        filename = filename +'_thumbnail' +ext
        new_file = storage.open(filename, 'w')
        im.save(new_file, "PNG")
        new_file.close()
    except IOError as error:
        print("cannot create thumbnail for ", filename, 'error ', error)

堆栈

django 1.85

python 2.7.10

【问题讨论】:

    标签: django amazon-s3 pillow


    【解决方案1】:

    这非常有用,我用它找到了一种方法,可以在不直接使用 boto 的情况下从 django 将图像写入 s3。

    基本上 PIL 的 save() 方法不适用于 s3,但 default_storage.write() 方法可以。关键是使用 default_storage.write() 方法直接从 StringIO 内存文件写入二进制数据,如下所示:

    file_to_write.write(memory_file.getvalue())
    

    这是我在 django shell (python manage.py shell) 中运行的代码来测试这个:

    >>> from django.core.files.storage import default_storage as storage
    >>> from PIL import Image
    >>> import StringIO
    >>> i = storage.open('ImageToCreate.jpg','w+')
    >>> m = storage.open('ImageAlreadyOnS3.jpg','r')
    >>> im = Image.open(m)
    >>> im = im.resize((640,360),3)
    >>> sfile = StringIO.StringIO() #cStringIO works too
    >>> im.save(sfile, format="JPEG")
    >>> i.write(sfile.getvalue())
    >>> i.close()
    >>> m.close()
    

    它也适用于我的views.py。

    我发现它很有用,因为它既适用于远程 s3 存储,也适用于我的本地开发环境(我笔记本电脑上的一个文件夹)。

    【讨论】:

    • 这是我在这里看到的最有用的答案之一,不敢相信我是第一个支持它的人
    【解决方案2】:

    请注意,对于 Python3,您可能希望使用 BytesIO:

    from io import BytesIO
    
    # 'image' is a PIL image object.
    
    imageBuffer = BytesIO()
    image.save(imageBuffer, format=imageType)
    
    imageFile = default_storage.open(imageFileName, 'wb')
    imageFile.write(imageBuffer.getvalue())
    imageFile.flush()
    imageFile.close()
    

    【讨论】:

      【解决方案3】:

      经过几个小时的谷歌搜索,终于让它工作了,这主要归功于this 博客文章。

      tasks.py

      from celery import shared_task
      import os
      from django.core.files.storage import default_storage as storage
      from django.conf import settings
      import mimetypes
      import cStringIO
      from PIL import Image as PillowImage
      import boto
      from .models import Image
      
      @shared_task
      def create_thumbnails(pk):
          try:
              image = Image.objects.get(pk=pk)
          except Image.ObjectDoesNotExist:
              pass
          try:
              thumbnail_size = (450,200)
              filename, ext = os.path.splitext(image.image.name)
              filename = filename +'_thumbnail' +ext
              existing_file = storage.open(image.image.name, 'r')
              im = PillowImage.open(existing_file)
              im = im.resize(thumbnail_size, PillowImage.ANTIALIAS)
              memory_file = cStringIO.StringIO()
              mime = mimetypes.guess_type(filename)[0]
              plain_ext = mime.split('/')[1]
              im.save(memory_file, plain_ext)
      
              conn  = boto.connect_s3(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
              bucket = conn.get_bucket( 'yourbucketname', validate=False)
              k = bucket.new_key('media/' +filename)
              k.set_metadata('Content-Type', mime)
              k.set_contents_from_string(memory_file.getvalue())
              k.set_acl("public-read")
              memory_file.close()
          except Exception as error:
              print("cannot create thumbnail for ", filename, 'error ', error)
      

      【讨论】:

        【解决方案4】:

        我有同样的问题。

        default_storage.write() # It didn't work. it was not saving anything to s3 
        

        @RunLoop 的答案非常复杂,并且与我想用 Django 做的不同,所以我这样做了,它奏效了。

            import StringIO
            from PIL import Image
        

        首先,读取上传的文件

            image = request.FILES['image'].read()  #atleast in my case 
        

        创建一个类似对象的文件,以便我们可以使用 Image 来读取它

            image_file = StringIO.StringIO(image)
            thumbnail_image = Image.open(image_file)
        

        将图像调整为所需大小

            resized_thumbnail_image = thumbnail_image.resize((200, 200), Image.ANTIALIAS)
        

        创建另一个文件,如对象或内存文件,以便我们可以将 Image 实例写入其中并获取字符串值 - 应将其传递给默认存储

            resized_thumbnail_image_file = StringIO.StringIO()
            resized_thumbnail_image.save(resized_thumbnail_image_file, 'JPEG',quality=90)
        
            default_storage.save(save_path, ContentFile(resized_thumbnail_image_file.getvalue()))
        

        我的第一个详细答案,希望对您有所帮助。

        堆栈: 蟒蛇2.7 Django 1.8

        【讨论】:

          【解决方案5】:

          rotten 的回答更容易理解、更可行的例子

          from io import BytesIO
          from PIL import Image
          from django.core.files.base import ContentFile
          from django.core.files.storage import default_storage
          from django.contrib.staticfiles.storage import staticfiles_storage
          
          img = Image.new('RGB', (1920, 1080), color = 'red') # <-- use this
          #img = Image.open(r'C:\Users\you\Pictures\profile.jpg') # <-- or a file on your computer
          
          buffer = BytesIO()
          img.save(buffer, format='JPEG')
          
          fp = '{path}{name}'.format(path='adminz/profile/', name='default.jpg')
          f = default_storage.open(fp, 'wb')
          f.write(buffer.getvalue())
          f.close()
          

          我正在使用 Django Storages,这就是我需要弄清楚这一点的原因。我包括了额外未使用的导入,因为您在处理这个主题时经常会找到了解它们的理由。

          我在项目中使用的另一个例子:

          image = Image.open(photo) # <- open
          cropped_image = image.crop((x, y, w + x, h + y)) # <- do something
          with BytesIO() as f:
              cropped_image.save(f, format=settings.THUMBNAIL_FORMAT, quality=95) # <- save to buffer
              img = f.getvalue()
          
          post.featured_image.save(photo.name,
                                   content=ContentFile(img))
          

          【讨论】:

            猜你喜欢
            • 2017-08-18
            • 2023-03-24
            • 2015-06-27
            • 1970-01-01
            • 2014-12-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多