【问题标题】:How do you convert a PIL `Image` to a Django `File`?如何将 PIL `Image` 转换为 Django`File`?
【发布时间】:2011-04-13 00:00:30
【问题描述】:

我正在尝试将 UploadedFile 转换为 PIL Image 对象以对其进行缩略图,然后将我的缩略图函数返回的 PIL Image 对象转换回 File 对象。我该怎么做?

【问题讨论】:

标签: python django python-imaging-library django-file-upload django-uploads


【解决方案1】:

我必须分几步完成,php 中的 imagejpeg() 需要类似的过程。并不是说没有办法将内容保存在内存中,但这种方法为您提供了对原始图像和拇指的文件引用(通常是一个好主意,以防您必须返回并更改拇指大小)。

  1. 保存文件
  2. 使用 PIL 从文件系统打开它,
  3. 使用 PIL 保存到临时目录,
  4. 然后以 Django 文件的形式打开以使其正常工作。

型号:

class YourModel(Model):
    img = models.ImageField(upload_to='photos')
    thumb = models.ImageField(upload_to='thumbs')

用法:

#in upload code
uploaded = request.FILES['photo']
from django.core.files.base import ContentFile
file_content = ContentFile(uploaded.read())
new_file = YourModel() 
#1 - get it into the DB and file system so we know the real path
new_file.img.save(str(new_file.id) + '.jpg', file_content)
new_file.save()

from PIL import Image
import os.path

#2, open it from the location django stuck it
thumb = Image.open(new_file.img.path)
thumb.thumbnail(100, 100)

#make tmp filename based on id of the model
filename = str(new_file.id)

#3. save the thumbnail to a temp dir

temp_image = open(os.path.join('/tmp',filename), 'w')
thumb.save(temp_image, 'JPEG')

#4. read the temp file back into a File
from django.core.files import File
thumb_data = open(os.path.join('/tmp',filename), 'r')
thumb_file = File(thumb_data)

new_file.thumb.save(str(new_file.id) + '.jpg', thumb_file)

【讨论】:

  • 高度未优化,你在步骤 1,2,3,4 中进行 IO 操作。
【解决方案2】:

无需写回文件系统,然后通过 open 调用将文件带回内存的方法是使用 StringIO 和 Django InMemoryUploadedFile。这是一个关于如何执行此操作的快速示例。这假设您已经有一个名为“thumb”的缩略图:

import StringIO

from django.core.files.uploadedfile import InMemoryUploadedFile

# Create a file-like object to write thumb data (thumb data previously created
# using PIL, and stored in variable 'thumb')
thumb_io = StringIO.StringIO()
thumb.save(thumb_io, format='JPEG')

# Create a new Django file-like object to be used in models as ImageField using
# InMemoryUploadedFile.  If you look at the source in Django, a
# SimpleUploadedFile is essentially instantiated similarly to what is shown here
thumb_file = InMemoryUploadedFile(thumb_io, None, 'foo.jpg', 'image/jpeg',
                                  thumb_io.len, None)

# Once you have a Django file-like object, you may assign it to your ImageField
# and save.
...

如果您需要更多说明,请告诉我。我现在在我的项目中工作,使用 django-storages 上传到 S3。这花了我一天的大部分时间在此处正确找到解决方案。

【讨论】:

  • 漂亮而简单。在 pre_save 信号中完美运行。
  • 我发现你可以通过使用 Django ContentFile 类来减少更多的工作量。在这种情况下,从 django.core.files.base 导入 ContentFile 然后你会这样做: thumb_file = ContentFile(thumb_io.getvalue())
  • @Bialecki 没关系,我找到了答案。我们在保存到模型图像字段时指定名称,如下所示:mymodel.myimagefield.save(myfilename, imagecontentfile, save=True)
  • @madzohan 你是如何让StringIO 在 Python 3 中工作的?我不得不使用BytesIO(并且由于它没有 len() 函数,因此必须使用 tell() 来确定它的长度。
  • @JohnC 是的,你应该使用BytesIO 在这里尝试我的答案stackoverflow.com/a/30435175/3033586 顺便说一句,之前的评论已过时并被删除
【解决方案3】:

这是一个可以做到这一点的应用程序:django-smartfields

from django.db import models

from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import ImageProcessor

class ImageModel(models.Model):
    image = fields.ImageField(dependencies=[
        FileDependency(processor=ImageProcessor(
            scale={'max_width': 150, 'max_height': 150}))
    ])

如果您想保留旧文件,请确保将keep_orphans=True 传递给该字段,否则它们会在替换时被清理。

【讨论】:

    【解决方案4】:

    对于那些使用django-storages/-redux 将图像文件存储在 S3 上的用户,这是我采用的路径(下面的示例创建现有图像的缩略图):

    from PIL import Image
    import StringIO
    from django.core.files.storage import default_storage
    
    try:
        # example 1: use a local file
        image = Image.open('my_image.jpg')
        # example 2: use a model's ImageField
        image = Image.open(my_model_instance.image_field)
        image.thumbnail((300, 200))
    except IOError:
        pass  # handle exception
    
    thumb_buffer = StringIO.StringIO()
    image.save(thumb_buffer, format=image.format)
    s3_thumb = default_storage.open('my_new_300x200_image.jpg', 'w')
    s3_thumb.write(thumb_buffer.getvalue())
    s3_thumb.close()
    

    【讨论】:

      【解决方案5】:

      这是 python 3.5django 1.10

      的实际工作示例

      在views.py中:

      from io import BytesIO
      from django.core.files.base import ContentFile
      from django.core.files.uploadedfile import InMemoryUploadedFile
      
      def pill(image_io):
          im = Image.open(image_io)
          ltrb_border = (0, 0, 0, 10)
          im_with_border = ImageOps.expand(im, border=ltrb_border, fill='white')
      
          buffer = BytesIO()
          im_with_border.save(fp=buffer, format='JPEG')
          buff_val = buffer.getvalue()
          return ContentFile(buff_val)
      
      def save_img(request)
          if request.POST:
             new_record = AddNewRecordForm(request.POST, request.FILES)
             pillow_image = pill(request.FILES['image'])
             image_file = InMemoryUploadedFile(pillow_image, None, 'foo.jpg', 'image/jpeg', pillow_image.tell, None)
             request.FILES['image'] = image_file  # really need rewrite img in POST for success form validation
             new_record.image = request.FILES['image']
             new_record.save()
             return redirect(...)
      

      【讨论】:

        【解决方案6】:

        为 Python 3+ 整合 cmets 和更新

        from io import BytesIO
        from django.core.files.base import ContentFile
        import requests
        
        # Read a file in
        
        r = request.get(image_url)
        image = r.content
        scr = Image.open(BytesIO(image))
        
        # Perform an image operation like resize:
        
        width, height = scr.size
        new_width = 320
        new_height = int(new_width * height / width)
        img = scr.resize((new_width, new_height))
        
        # Get the Django file object
        
        thumb_io = BytesIO()
        img.save(thumb_io, format='JPEG')
        photo_smaller = ContentFile(thumb_io.getvalue())
        

        【讨论】:

          【解决方案7】:

          为那些像我一样想要将它与 Django 的FileSystemStorage 结合起来的人完成: (我在这里做的是上传一张图片,将其调整为二维并保存两个文件。

          utils.py

          def resize_and_save(file):
              size = 1024, 1024
              thumbnail_size = 300, 300
              uploaded_file_url = getURLforFile(file, size, MEDIA_ROOT)
              uploaded_thumbnail_url = getURLforFile(file, thumbnail_size, THUMBNAIL_ROOT)
              return [uploaded_file_url, uploaded_thumbnail_url]
          
          def getURLforFile(file, size, location):
              img = Image.open(file)
              img.thumbnail(size, Image.ANTIALIAS)
              thumb_io = BytesIO()
              img.save(thumb_io, format='JPEG')
              thumb_file = InMemoryUploadedFile(thumb_io, None, file.name, 'image/jpeg', thumb_io.tell, None)
              fs = FileSystemStorage(location=location)
              filename = fs.save(file.name, thumb_file)
              return fs.url(filename)  
          

          views.py

          if request.FILES:
                  fl, thumbnail = resize_and_save(request.FILES['avatar'])
                  #delete old profile picture before saving new one
                  try:
                      os.remove(BASE_DIR + user.userprofile.avatarURL)
                  except Exception as e:
                      pass         
                  user.userprofile.avatarURL = fl
                  user.userprofile.thumbnailURL = thumbnail
                  user.userprofile.save()
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-11-15
            • 2017-12-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-06-23
            • 2021-08-05
            • 2010-10-28
            相关资源
            最近更新 更多