如何将一个PIL ' Image '转换为Django ' File ' ?

时间:2022-07-26 00:23:18

I'm trying to convert an UploadedFile to a PIL Image object to thumbnail it, and then convert the PIL Image object that my thumbnail function returns back into a File object. How can I do this?

我正在尝试将上传的文件转换为PIL Image对象来缩略图,然后将我的thumbnail函数返回的PIL Image对象转换为File对象。我该怎么做呢?

6 个解决方案

#1


90  

The way to do this without having to write back to the filesystem, and then bring the file back into memory via an open call, is to make use of StringIO and Django InMemoryUploadedFile. Here is a quick sample on how you might do this. This assumes that you already have a thumbnailed image named 'thumb':

实现这一点的方法是使用StringIO和Django InMemoryUploadedFile。这里有一个简单的例子,告诉你怎么做。假设您已经有一个名为“拇指”的缩略图:

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.
...

Let me know if you need more clarification. I have this working in my project right now, uploading to S3 using django-storages. This took me the better part of a day to properly find the solution here.

如果你需要更多的说明,请告诉我。我现在在我的项目中使用django存储将其上传到S3。我花了一天的大部分时间才找到了正确的解决方案。

#2


12  

I've had to do this in a few steps, imagejpeg() in php requires a similar process. Not to say theres no way to keep things in memory, but this method gives you a file reference to both the original image and thumb (usually a good idea in case you have to go back and change your thumb size).

我必须通过一些步骤来实现这一点,php中的imagejpeg()需要类似的过程。并不是说没有办法把东西保存在内存中,但是这个方法给了你一个文件引用,它可以同时引用原始图像和拇指(通常是一个好主意,以防你不得不回去改变你的拇指大小)。

  1. save the file
  2. 保存文件
  3. open it from filesystem with PIL,
  4. 用PIL打开它,
  5. save to a temp directory with PIL,
  6. 保存到包含PIL的临时目录,
  7. then open as a Django file for this to work.
  8. 然后以Django文件的形式打开以使其工作。

Model:

模型:

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

Usage:

用法:

#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)

#3


3  

This is actual working example for python 3.5 and django 1.10

这是python 3.5和django 1.10的实际工作示例

in views.py:

在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(...)

#4


2  

Putting together comments and updates for Python 3+

将Python 3+的注释和更新放在一起

from io import BytesIO
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 = io.BytesIO()
img.save(thumb_io, format='JPEG')
photo_smaller = ContentFile(thumb_io.getvalue())

#5


1  

Here is an app that can do that: django-smartfields

这里有一个应用程序可以做到这一点: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}))
    ])

Make sure to pass keep_orphans=True to the field, if you want to keep old files, otherwise they are cleaned up upon replacement.

如果您想保存旧文件,请确保将keep_孤儿=True传递给该字段,否则将在替换时清除它们。

#6


1  

For those using django-storages/-redux to store the image file on S3, here's the path I took (the example below creates a thumbnail of an existing image):

对于那些使用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()

#1


90  

The way to do this without having to write back to the filesystem, and then bring the file back into memory via an open call, is to make use of StringIO and Django InMemoryUploadedFile. Here is a quick sample on how you might do this. This assumes that you already have a thumbnailed image named 'thumb':

实现这一点的方法是使用StringIO和Django InMemoryUploadedFile。这里有一个简单的例子,告诉你怎么做。假设您已经有一个名为“拇指”的缩略图:

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.
...

Let me know if you need more clarification. I have this working in my project right now, uploading to S3 using django-storages. This took me the better part of a day to properly find the solution here.

如果你需要更多的说明,请告诉我。我现在在我的项目中使用django存储将其上传到S3。我花了一天的大部分时间才找到了正确的解决方案。

#2


12  

I've had to do this in a few steps, imagejpeg() in php requires a similar process. Not to say theres no way to keep things in memory, but this method gives you a file reference to both the original image and thumb (usually a good idea in case you have to go back and change your thumb size).

我必须通过一些步骤来实现这一点,php中的imagejpeg()需要类似的过程。并不是说没有办法把东西保存在内存中,但是这个方法给了你一个文件引用,它可以同时引用原始图像和拇指(通常是一个好主意,以防你不得不回去改变你的拇指大小)。

  1. save the file
  2. 保存文件
  3. open it from filesystem with PIL,
  4. 用PIL打开它,
  5. save to a temp directory with PIL,
  6. 保存到包含PIL的临时目录,
  7. then open as a Django file for this to work.
  8. 然后以Django文件的形式打开以使其工作。

Model:

模型:

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

Usage:

用法:

#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)

#3


3  

This is actual working example for python 3.5 and django 1.10

这是python 3.5和django 1.10的实际工作示例

in views.py:

在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(...)

#4


2  

Putting together comments and updates for Python 3+

将Python 3+的注释和更新放在一起

from io import BytesIO
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 = io.BytesIO()
img.save(thumb_io, format='JPEG')
photo_smaller = ContentFile(thumb_io.getvalue())

#5


1  

Here is an app that can do that: django-smartfields

这里有一个应用程序可以做到这一点: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}))
    ])

Make sure to pass keep_orphans=True to the field, if you want to keep old files, otherwise they are cleaned up upon replacement.

如果您想保存旧文件,请确保将keep_孤儿=True传递给该字段,否则将在替换时清除它们。

#6


1  

For those using django-storages/-redux to store the image file on S3, here's the path I took (the example below creates a thumbnail of an existing image):

对于那些使用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()