Python 如何将 PIL `Image` 转换为 Django `File`?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3723220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you convert a PIL `Image` to a Django `File`?
提问by orokusaki
I'm trying to convert an UploadedFileto a PIL Imageobject to thumbnail it, and then convert the PIL Imageobject that my thumbnail function returns back into a Fileobject. How can I do this?
我正在尝试将 an 转换UploadedFile为 PILImage对象以对其进行缩略图,然后将Image缩略图函数返回的 PIL对象转换回一个File对象。我怎样才能做到这一点?
采纳答案by Skitz
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':
无需写回文件系统,然后通过 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.
...
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-storages 上传到 S3。这花了我一天的大部分时间才能在这里找到解决方案。
回答by Lincoln B
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() 需要类似的过程。并不是说没有办法将内容保存在内存中,但这种方法为您提供了对原始图像和拇指的文件引用(通常是一个好主意,以防您必须返回并更改拇指大小)。
- save the file
- open it from filesystem with PIL,
- save to a temp directory with PIL,
- then open as a Django file for this to work.
- 保存文件
- 使用 PIL 从文件系统打开它,
- 使用 PIL 保存到临时目录,
- 然后作为 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)
回答by lehins
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=Trueto the field, if you want to keep old files, otherwise they are cleaned up upon replacement.
keep_orphans=True如果要保留旧文件,请确保传递到该字段,否则它们会在替换时被清理。
回答by Cloud Artisans
For those using django-storages/-reduxto 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()
回答by dEll
This is actual working example for python 3.5and 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(...)
回答by Rob
Putting together comments and updates for Python 3+
将 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())
回答by mrj
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.
为那些像我一样想要将它与 Django 结合的人完成FileSystemStorage:(我在这里所做的是上传图像,将其调整为二维并保存这两个文件。
utils.py
实用程序.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
在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()

