Загрузка файла через Angular (с использованием FormData) в django-rest-framework

Я перерыл все возможные вопросы на StackOverflow и сообщения в блогах, и все еще сталкиваюсь с проблемами.

Я пытаюсь загрузить файл в конечную точку django-rest-framework с помощью Angular 12.

Это код фронтенда:

uploadCameraImage(id: CameraInterface["id"], image: File): Observable<CameraInterface> {
  const formData: FormData = new FormData();
  formData.append("image", image);

  const httpOptions = {
    headers: new HttpHeaders({
      "Content-Type": "multipart/form-data",
      "Content-Disposition": `attachment; filename=${image.name}`
    })
  };

  return this.http.put<CameraInterface>(`${this.configUrl}/camera/${id}/image/`, formData, httpOptions);
}

Вот соответствующие django-rest-framework части:

# This is the main base serializer used for other operations
class EquipmentItemSerializer(serializers.Serializer):
    class Meta:
        fields = [
            'created_by',
            'created',
            'updated',
            'brand',
            'name',
            'image',
        ]
        read_only_fields = ['image']
        abstract = True


# This is the base serializer only used for the image upload
class EquipmentItemImageSerializer(serializers.Serializer):
    class Meta:
        fields = ['image']
        abstract = True


# This is the main serializer used for all other operations (create/delete...)
class CameraSerializer(EquipmentItemSerializer, serializers.ModelSerializer):
    class Meta:
        model = Camera
        fields = '__all__'


# This is the serializer used for the image upload in the @action below
class CameraImageSerializer(EquipmentItemImageSerializer, serializers.ModelSerializer):
    class Meta:
        model = Camera
        fields = EquipmentItemImageSerializer.Meta.fields


# This is the base view set
class EquipmentItemViewSet(viewsets.ModelViewSet):
    renderer_classes = [BrowsableAPIRenderer, CamelCaseJSONRenderer]
    permission_classes = [IsEquipmentModeratorOrReadOnly]
    http_method_names = ['get', 'post', 'head', 'put', 'patch']

    def get_queryset(self):
        q = self.request.GET.get('q')
        manager = self.get_serializer().Meta.model.objects
        return manager.all()

    def image_upload(self, request, pk):
        obj = self.get_object()
        serializer = self.serializer_class(obj, data=request.data, partial=True)

        # NOTE: I have to do this because FileUploadParser hardcodes 'file' while my model has 'image' :(
        serializer.initial_data['image'] = serializer.initial_data['file']

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)

        return Response(serializer.errors, HTTP_400_BAD_REQUEST)

    class Meta:
        abstract = True


# This the main view set
class CameraViewSet(EquipmentItemViewSet):
    serializer_class = CameraSerializer

    @action(
        detail=True,
        methods=['PUT'],
        # NOTE THE DIFFERENT SERIALIZER FOR THE ACTION
        serializer_class=CameraImageSerializer,
        # I'VE TRIED ALL COMBINATIONS OF PARSERS INCLUIDING FormParser AND MultiPartParser
        parser_classes=[FileUploadParser],
    )
    def image(self, request, pk):
        return super(CameraViewSet, self).image_upload(request, pk)

При использовании этого кода результирующее сообщение об ошибке заключается в том, что загружаемый файл не является допустимым изображением. Я отладил код, и он сводится к проверке Django с помощью ImageField, используя PIL для открытия изображения.

Я полагаю, что файл, сохраненный как Django TemporaryFile, - это не только PNG, который я отправляю, но и все тело моего PUT запроса. Вот начало:

-----------------------------127295088417990690601368813565
Content-Disposition: form-data; name="image"; filename="9f1971c6-b8e6-4c5a-bd55-958b7fdd6ab1.png"
Content-Type: image/png

PNG

(binary data follows)

Если я добавлю FormParser в парсер моих image @action, я все равно получу такое же поведение. Если я также добавлю MultiPartParser, я получу пустые request.data и request.files.

Может кто-нибудь помочь?

Вернуться на верх