在Django REST框架中上传的文件中包含的标题内容

fjnneemd  于 2023-06-25  发布在  Go
关注(0)|答案(3)|浏览(124)

我在上传django api文件时遇到了问题。当我使用FileUploadParser上传文件时,一切都很顺利,但文件上传包含请求的头部,例如Content-Disposition,当我试图打开上传的文件时,它被破坏了。我找了一段时间的解决办法,但没有机会。决定使用MultiPartparser,但这样在www.example.com dict中就什么都不包含了。我该如何解决这个问题?request.data谢谢你的任何提示。这是我目前为止得到的代码 Thanks for any hint. Here's the code I have so far

class EstablishmentMediaUploadView(views.APIView):
    permission_classes = (IsAuthenticated,)
    authentication_class = JSONWebTokenAuthentication
    parser_classes = (FileUploadParser,)
    serializer_class = MediaSerializer
    name = 'establishment-media-file-upload'

    def put(self, request, **kwargs):
        print(request.data)
        if 'file' not in request.data:
            raise ParseError("Empty media file for establishment")
        establishmentid = kwargs.get('establishmentid')
        if establishmentid is None:
            return Response({"error": "You didn't specify the establishmentid"}, status=400)
        mediaFile = request.data.get('file')
        media = Media.objects.create(mediatitle=mediaFile.name)
        establishment = Establishment.objects.get(id=establishmentid)
        media.establishmentlogo.save(mediaFile.name, mediaFile, save=False)
        media.establishment = establishment
        media.save()
        return Response({"message": "Logo added for this establishment"}, status=200)

我首先通过从Insomnia api测试客户端和Vscode thunder客户端扩展上传来测试它。对于这两个标题都包括在内。
然后我在我的Angular 前端做了测试。下面是负责上传的服务方法的代码:

setEstablishmentLogo(establishment: Establishment, media: Media): Observable<Object> {
    let formdata = new FormData();
    formdata.set("establishmentlogo", media.establishmentlogo);
    var url = `${endpoints.establishment_media_upload_uri_base}/${establishment.id}`;
    console.log(url)
    return this.http.put(url, formdata, {
      headers: {
        "Accept": "*/*",
        "Content-Disposition": `attachment; filename=${media.establishmentlogo.name}`,
        'Authorization': `Bearer ${token}`
      }
    });
  }

有同样的行为。也许我在这件事上有些地方错了。但我想不出来。

pwuypxnk

pwuypxnk1#

我使用Flutter框架,我从移动的上传图像到Django,猜猜看,它更新图像字段而不做任何事情。只要确保你从哪里上传这个图像,就可以在那个框架中的http库中相应地上传。

mgdq6dx1

mgdq6dx12#

所以为了避免这种头痛给另一个人。这是我最后发现的。
我在上传视图中使用了FileUploadParser。但问题是,当使用“FileUploadParser”时,request.data的dict会被上传的内容填充。在这种情况下,上传的内容,当它来自像Postman或Insomnia或VSCode的Thunder Client这样的客户端时,它包含您上传的单个文件(我们假设您在这些客户端中选择了二进制请求选项),这样,不会发生任何错误,文件是安全的。但是当你从浏览器上传文件时,假设从angular和视图中的parser_class设置为FileUploadParser,那么你将成功上传文件,但它会被破坏,因为整个请求(文件+头和浏览器边界的东西)被FileUploadParser解析为单个文件(这确实是它的工作),因此你最终在服务器或后端上损坏了文件。因此,解决这个问题的方法是将parser_classes设置为MultipartParser和FormParser(可选),这样您上传的文件就可以了。而且,你不需要指定Content-Disposition:附件; filename ='some file name'在你的请求头中。请记住,当你使用FileUploadParser时,你的文件在request.data ['file']中,而当它是MultiPartParser(和FormParser)时,文件在request.FILES ['file']中。
说到这里,我在问题中发布的上传视图的工作版本如下所示:

class EstablishmentMediaUploadView(views.APIView):
    permission_classes = (IsAuthenticated,)
    authentication_class = JSONWebTokenAuthentication
    parser_classes = (MultiPartParser, FormParser,)
    name = 'mtp-establishment-media-file-upload'

    def post(self, request, *args, **kwargs):
        if 'file' not in request.FILES:
            return Response({"message": "Please provide a file"}, status=status.HTTP_400_BAD_REQUEST)
        establishmentid = kwargs.get('establishmentid')
        if establishmentid is None:
            return Response({"error": "You didn't specify the establishmentid"}, status=status.HTTP_400_BAD_REQUEST)
        mediaFile = request.FILES['file']
        media = Media.objects.create(mediatitle=mediaFile.name)
        media.establishmentlogo.save(mediaFile.name, mediaFile, save=True)
        try:
            establishment = Establishment.objects.get(id=establishmentid)
        except Establishment.DoesNotExist:
            return Response({"error": "Establishment does not exist"}, status=status.HTTP_404_NOT_FOUND)
        media.establishment = establishment
        media.save()
        return Response({"message": "Logo added for this establishment"}, status=status.HTTP_201_CREATED)
c9x0cxw0

c9x0cxw03#

使用FileUploadParser时,请确保POST原始二进制数据:
从命令行:

$ curl -X POST --data-binary '@./video.mp4' -H 'Content-Disposition: attachment; filename=ignored.mp4' <url>

python:

import requests
requests.post(
    <url>, 
    data=open('./video.mp4', 'rb'),
    headers={'Content-Disposition': 'attachment; filename=ignored.mp4'}
)

然后,该原始数据将被流传输到磁盘或流传输到内存,具体取决于有效负载的大小。然后,您可以迭代字节块并对数据执行一些操作:

from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from rest_framework.views import APIView

class VideoView(APIView):
    parser_classes = APIView.parser_classes + [FileUploadParser]

    def post(self, request):
        """Handle POST request containing raw binary data.

        Args:
            request: The prepared request from drf.

        Notes:
            Make sure to add the header: `Content-Disposition: attachment; filename=video.mp4`.
            The filename passed in this header is ignored.
            The Content-Type header is also ignored (*/* is accepted).

        Examples:
            $ curl -X POST --data-binary '@./video.mp4' -H 'Content-Disposition: attachment; filename=ignored.mp4' <url>
            >>> import requests; requests.post(<url>, data=open('./video.mp4', 'rb'), headers={'Content-Disposition': 'attachment; filename=ignored.mp4'})

        References:
            https://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser

        Returns:
            Response(status=201)
        """
        if "file" not in request.data:
            return Response(
                "Bad data in POST request: expected raw binary data and a `Content-Disposition: attachment; filename=video.mp4` header",
                status=400,
            )

        chunk_iter = request.data["file"].chunks(1 << 19)  # read 512kB chunks
        for chunk in chunk_iter:
            ...  # do something with these chunks of bytes

相关问题