UnicodeDecodeError: 'UTF-8' codec can't decode byte 0xff in position 0: invalid start byte. DRF

There is a backend on django and a postgres DB that stores data about the item, including pictures. And there is a problem with getting a picture from the database, i've looked at enough solutions, but none of them work.

serializers.py

import base64
import uuid
import imghdr

class Base64ImageField(serializers.ImageField):
    def to_internal_value(self, data):
        if isinstance(data, str) and 'data:' in data and ';base64,' in data:
            header, data = data.split(';base64,')
            try:
                decoded_file = base64.b64decode(data)
            except (TypeError, ValueError):
                self.fail('invalid_image')
            file_name = str(uuid.uuid4())[:12] 
            file_extension = self.get_file_extension(file_name, decoded_file)
            complete_file_name = f"{file_name}.{file_extension}"
            data = ContentFile(decoded_file, name=complete_file_name)

        return super(Base64ImageField, self).to_internal_value(data)

    def get_file_extension(self, file_name, decoded_file):
        extension = imghdr.what(file_name, decoded_file)
        return extension or 'jpg'

class CreateItemSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    photo = Base64ImageField(required=True)
    class Meta:
        model = CreateItem
        fields = '__all__'


    def create(self, validated_data):
        items = CreateItem.object.create_item(
            name = validated_data.get('name'),
            price = validated_data.get('price'),
            description = validated_data.get('description'),
            type_item = validated_data.get('type_item'),
            photo=validated_data.get('photo')
        )
        return items

views.py


class GetItemView(APIView):
    serializer_class = CreateItemSerializer
    def get(self, request):
        items = CreateItem.object.all()  
        response_data = {
            'items': [
                {   
                    'photo': item.photo,
                    'name': item.name,
                    'description': item.description,
                    'type_item': item.type_item,
                    'price': item.price,
                }
                for item in items
            ]
        }
        return Response(response_data, status=status.HTTP_200_OK)

full error traceback

Internal Server Error: /api/v1/item/items-get/
Traceback (most recent call last):
  File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
               ^^^^^^^^^^^^^^^^^
  File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/django/template/response.py", line 114, in render
    self.content = self.rendered_content
                   ^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/response.py", line 74, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/renderers.py", line 100, in render
    ret = json.dumps(
          ^^^^^^^^^^^
  File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/utils/json.py", line 25, in dumps
    return json.dumps(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "/usr/lib/python3.12/json/encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
  File "/home/anton/Documents/e-m/e-market/backend/my_backend/venv_em/lib/python3.12/site-packages/rest_framework/utils/encoders.py", line 52, in default
    return obj.decode()
           ^^^^^^^^^^^^
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
[04/Oct/2024 18:59:30] "GET /api/v1/item/items-get/ HTTP/1.1" 500 115134

Maybe the problem is with the desirealizer, but if so I don't know how to fix it.

The error you're encountering is due to how the image data is handled in your serializer and view. The issue is that when you're trying to serialize the image field for the response, it's attempting to decode the binary image data as UTF-8 text, which is not correct. Let's modify your code to handle this properly

Serializers.py

from rest_framework import serializers
from django.core.files.base import ContentFile
import base64
import uuid
import imghdr

class Base64ImageField(serializers.ImageField):
    def to_internal_value(self, data):
        if isinstance(data, str) and 'data:' in data and ';base64,' in data:
            header, data = data.split(';base64,')
            try:
                decoded_file = base64.b64decode(data)
            except (TypeError, ValueError):
                self.fail('invalid_image')
            file_name = str(uuid.uuid4())[:12] 
            file_extension = self.get_file_extension(file_name, decoded_file)
            complete_file_name = f"{file_name}.{file_extension}"
            data = ContentFile(decoded_file, name=complete_file_name)

        return super(Base64ImageField, self).to_internal_value(data)

    def get_file_extension(self, file_name, decoded_file):
        extension = imghdr.what(file_name, decoded_file)
        return extension or 'jpg'

    def to_representation(self, value):
        if not value:
            return None
        return value.url  # Return the URL of the image instead of the file object

class CreateItemSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    photo = Base64ImageField(required=True)
    
    class Meta:
        model = CreateItem
        fields = '__all__'

    def create(self, validated_data):
        return CreateItem.objects.create(**validated_data)


views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import CreateItem
from .serializers import CreateItemSerializer

class GetItemView(APIView):
    def get(self, request):
        items = CreateItem.objects.all()
        serializer = CreateItemSerializer(items, many=True)
        return Response({'items': serializer.data}, status=status.HTTP_200_OK)
Back to Top