Я искал решение для изменения размера загруженной фотографии перед сохранением.Здесь и там много и много информации (в StackOverflow).Пока нет полного решения.Вот мое окончательное решение, которое, я думаю, работает для людей, которые хотят его.
Highlight Development
- Использование подушки для обработки изображений (требуется два пакета: libjpeg-dev, zlib1g-dev)
- Использование Model и ImageField в качестве хранилища
- Использование HTTP POST или PUT с multipart / form
- Нет необходимости сохранять файл на диск вручную.
- Создание нескольких разрешений и сохранение их размеров.
- Не изменялась сама модель
Установка подушки
$ sudo apt-get install libjpeg-dev
$ sudo apt-get install zlib1g-dev
$ pip install -I Pillow
myapp / models.py
from django.db import models
class Post(models.Model):
caption = models.CharField(max_length=100, default=None, blank=True)
image_w = models.PositiveIntegerField(default=0)
image_h = models.PositiveIntegerField(default=0)
image = models.ImageField(upload_to='images/%Y/%m/%d/', default=None,
blank=True, width_field='image_w', height_field='image_h')
thumbnail = models.ImageField(upload_to='images/%Y/%m/%d/', default=None,
blank=True)
Эта модель сохраняет фотографию с миниатюрой и необязательной подписью.Это должно быть похоже на реальный вариант использования.
Наша цель - изменить размер фотографии до 640x640 и создать миниатюру 150x150.Нам также необходимо вернуть размер фотографии в нашем веб-API.image_w
и image_h
- это кэшированное измерение в таблице.Обратите внимание, что нам нужно добавить квоту, когда мы объявляем ImageField.Тем не менее, thumbnail
не нуждается в поле ширины и высоты (так что вы можете видеть различное).
Это модель.Нам не нужно переопределять или добавлять какие-либо функции в модель.
myapp / serializers.py
Мы будем использовать ModelSerializer
для обработки входящих данныхиз HTTP POST.
from rest_framework import serializers
from myapp.models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('caption',)
Мы не хотим, чтобы сериализатор обрабатывал загруженную фотографию - мы сделаем это сами.Таким образом, нет необходимости включать 'image' в поля.
myapp / views.py
@api_view(['POST'])
@parser_classes((MultiPartParser,))
def handle_uploaded_image(request):
# process images first. if error, quit.
if not 'uploaded_media' in request.FILES:
return Response({'msg': 'Photo missing.'}, status.HTTP_400_BAD_REQUEST)
try:
im = Image.open(StringIO(request.FILES['uploaded_media'].read()))
except IOError:
return Response({'msg': 'Bad image.'}, status.HTTP_400_BAD_REQUEST)
serializer = PostSerializer(data=request.DATA, files=request.FILES)
if not serializer.is_valid():
return Response({'msg': serializer.errors}, status.HTTP_400_BAD_REQUEST)
post = Post.create()
if serializer.data['caption'] is not None:
post.caption = serializer.data['caption']
filename = uuid.uuid4()
name = '%s_0.jpg' % (filename)
post.image.save(name=name, content=resize_image(im, 640))
name = '%s_1.jpg' % (filename)
post.thumbnail.save(name=name, content=resize_image(im, 150))
post.save()
return Response({'msg': 'success',
'caption': post.caption,
'image': {
'url': request.build_absolute_uri(post.image.url),
'width': post.image_w,
'height': post.image_h,
}
'thumbnail': request.build_absolute_uri(post.thumbnail.url),
}, status.HTTP_201_CREATED)
вспомогательные функции в общем py
def resize_image(im, edge):
(width, height) = im.size
(width, height) = scale_dimension(w, h, long_edge=edge)
content = StringIO()
im.resize((width, height), Image.ANTIALIAS).save(fp=content, format='JPEG', dpi=[72, 72])
return ContentFile(content.getvalue())
def scale_dimension(width, height, long_edge):
if width > height:
ratio = long_edge * 1. / width
else:
ratio = long_edge * 1. / height
return int(width * ratio), int(height * ratio)
Здесь мы не используем Image.thumbnail, потому что это изменит исходное изображение.Этот код подходит, если мы хотим хранить несколько разрешений (например, с низким и высоким разрешением для разных целей).Я обнаружил, что изменение размера входящего сообщения в два раза ухудшит качество.
Мы также не сохраняем изображение непосредственно на диск.Мы помещаем изображение через ContentFile (объект File для содержимого в памяти) в ImageField и позволяем ImageField выполнять свою работу.Мы хотим, чтобы несколько копий изображения имели одно и то же имя файла с разным постфиксом.
Кредиты
Вот ссылки на коды, на которые я ссылался при создании этого решения:
Если вы также хотите проверить изображение, посмотрите это: https://stackoverflow.com/a/20762344/3731039