如何将 PIL `Image` 转换为 Django `File`?

发布于 2024-09-24 02:52:12 字数 149 浏览 13 评论 0原文

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

扎心 2024-10-01 02:52:12

无需写回文件系统,然后通过 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。我花了一天的时间才在这里正确找到解决方案。

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':

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.

抹茶夏天i‖ 2024-10-01 02:52:12

我必须通过几个步骤来完成此操作,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)

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

  1. save the file
  2. open it from filesystem with PIL,
  3. save to a temp directory with PIL,
  4. then open as a Django file for this to work.

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)
你的往事 2024-10-01 02:52:12

这是views.py中python 3.5django 1.10的实际工作示例

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

This is actual working example for python 3.5 and django 1.10

in 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(...)
蔚蓝源自深海 2024-10-01 02:52:12

整理 Python 3+ 的评论和更新

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

Putting together comments and updates for Python 3+

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())
会傲 2024-10-01 02:52:12

对于那些像我一样想要将其与 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()

To complete for those who, like me, want to couple it with Django's FileSystemStorage:
(What I do here is upload an image, resize it to 2 dimensions and save both files.

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)  

In 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()
最美的太阳 2024-10-01 02:52:12

这是一个可以做到这一点的应用程序: 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=如果您想保留旧文件,则确实如此,否则它们会在替换时被清除。

Here is an app that can do that: 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.

羁拥 2024-10-01 02:52:12

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

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

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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文