首页 > 解决方案 > 通过 Angular(使用 FormData)将文件上传到 django-rest-framework

问题描述

我已经在网上翻遍了所有可能的 StackOverflow 问题和博客文章,但我仍然面临问题。

我正在尝试使用 Angular 12 将文件上传到django-rest-framework端点。

这是前端代码:

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)

使用此代码,生成的错误消息是我上传的文件不是有效图像。我深入调试了代码,它归结为用于打开图像DjangoImageField验证。PIL

我相信保存为 a 的文件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.dataand request.files

有人可以帮忙吗?

标签: djangoangulardjango-rest-framework

解决方案


推荐阅读