如果有图像,如何将 Django InMemoryUploadFile 传递给序列化程序?

How to pass Django InMemoryUploadFile to serializer in case with images?

我正在创建一个 API 使用 Django Rest Framework 来直接从文件或从 url 保存图像。我对第一种情况没有问题,但对第二种情况没有问题。所以,我的模型看起来像这样:

class Image(models.Model):
    picture = models.ImageField(upload_to="site_media", blank=True)
    url = models.URLField(null=True, blank=True, max_length=255)
    parent_picture = models.IntegerField(null=True, blank=True)


    @property
    def name(self):
        return os.path.split(self.picture.name)[-1]

    @property
    def width(self):
        return self.picture.width

    @property
    def height(self):
        return self.picture.height

接下来,我的序列化程序 class:

class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = [
            'id',
            'name',
            'url',
            'picture',
            'width',
            'height',
            'parent_picture'
        ]

主要思想是,如果您在 post 请求时传递 URL,API 需要从此 URL 下载图像并将其存储在数据库中。

为此,我重写了 Django Rest Framework 中的 create 方法 ModelViewSet class:

from io import BytesIO
import urllib
import os

from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image as PILImage
from rest_framework import status
from rest_framework import viewsets
from rest_framework.response import Response

from .models import Image
from .serializers import ImageSerializer

    
class ImageViewSet(viewsets.ModelViewSet):
    serializer_class = ImageSerializer
    queryset = Image.objects.all()
    
    def create(self, request, *args, **kwargs):
        if request.data["url"]:
            url = request.data["url"]

            image_extensions = {
                ".jpg": "JPEG",
                ".jpeg": "JPEG",
                ".png": "PNG",
                ".gif": "GIF"
            }

            image_path = urllib.parse.urlparse(url).path
            image_filename = image_path.split("/")[-1]
            extension = os.path.splitext(image_filename)[-1]

            image_bytes = BytesIO(urllib.request.urlopen(url).read())
            pillow_image = PILImage.open(image_bytes)
            
            buffer = BytesIO()
            pillow_image.save(buffer, format=image_extensions[extension])

            django_file = InMemoryUploadedFile(
                buffer, 
                "ImageField", 
                image_filename, 
                "image/"+extension[1:],
                len(buffer.getvalue()),
                None
            )

            serializer = self.get_serializer(data={"url": url, "picture": django_file})
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

但是当我使用Django时shell我发现序列化器不序列化InMemoryUploadFile,只序列化URL:

>>>django_file = InMemoryUploadFile(...)
>>>serializer = ImageSerializer(data={"url": url, "picture": django_file})
>>>serializer.data
{'url': 'some url', 'picture': None, 'parent_picture': None}  

但是如果我直接通过模型保存图片,就可以了。这是我在 views.py 文件末尾执行此操作的代码:

Image.objects.create(url=url, picture=django_file)
return Response("success!")

所以,我想知道问题出在哪里,我该如何解决?

我找到这个 link:

Pillow corrupt image for serializer

所以,当我添加

...
django_file.seek(0)

serializer = self.get_serializer(data={"url": url, "picture": django_file})
...

你的代码在我的机器上运行。