使用 boto 将 Pillow 文件保存到 S3
Saving a Pillow file to S3 with boto
我通过 s3boto 使用 Amazon S3 作为我的存储后端。我有一个带有 ImageField 的图像模型。当通过管理员上传图像时,它已成功上传到 S3。我现在要做的是使用 Pillow 创建缩略图 post-save。我已经验证缩略图是通过调用缩略图的 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
经过非常痛苦的谷歌搜索,终于让它工作了,这主要归功于 this 博客 post。
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)
这非常有用,我用它找到了一种无需直接使用 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 存储,也适用于我的本地开发环境(笔记本电脑上的一个文件夹)。
我遇到了同样的问题。
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_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()))
第一次详细回答,希望对你有帮助。
堆栈:
python 2.7
Django 1.8
请注意,对于 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()
一个比的答案
更容易理解、更可行的例子
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))
我通过 s3boto 使用 Amazon S3 作为我的存储后端。我有一个带有 ImageField 的图像模型。当通过管理员上传图像时,它已成功上传到 S3。我现在要做的是使用 Pillow 创建缩略图 post-save。我已经验证缩略图是通过调用缩略图的 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
经过非常痛苦的谷歌搜索,终于让它工作了,这主要归功于 this 博客 post。
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)
这非常有用,我用它找到了一种无需直接使用 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 存储,也适用于我的本地开发环境(笔记本电脑上的一个文件夹)。
我遇到了同样的问题。
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_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()))
第一次详细回答,希望对你有帮助。
堆栈: python 2.7 Django 1.8
请注意,对于 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()
一个比
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))