programing

Django Rest Framework를 사용하여 파일을 업로드하고 JSON payload를 전송하려면 어떻게 해야 합니까?

minimums 2023. 2. 22. 21:39
반응형

Django Rest Framework를 사용하여 파일을 업로드하고 JSON payload를 전송하려면 어떻게 해야 합니까?

JSON payload뿐만 아니라 파일을 수신할 수 있는 Django Rest Framework API 핸들러를 작성하려고 합니다.MultiPartParser를 핸들러 파서로 설정했습니다.

하지만 둘 다 할 수 없을 것 같아요.파일과 함께 payload를 멀티 파트 요구로 전송하면 request.data에서 JSON payload를 magred 방식으로 사용할 수 있습니다(첫 번째 텍스트 부분부터 첫 번째 콜론까지의 키, 나머지 부분은 데이터).표준 형식의 파라미터로 파라미터를 송신할 수 있습니다만, API의 나머지 부분은 JSON payload를 받아들이기 때문에 일관성을 유지하고 싶었습니다.요청입니다.몸을 일으킬 때 읽을 수 없다*** RawPostDataException: You cannot access body after reading from request's data stream

예를 들어 파일 및 요청 본문의 다음 페이로드가 있습니다.
{"title":"Document Title", "description":"Doc Description"}
이하가 됩니다.
<QueryDict: {u'fileUpload': [<InMemoryUploadedFile: 20150504_115355.jpg (image/jpeg)>, <InMemoryUploadedFile: Front end lead.doc (application/msword)>], u'{%22title%22': [u'"Document Title", "description":"Doc Description"}']}>

방법이 있을까요?케이크를 먹고, 보관하고, 살이 찌지 않게 할 수 있을까요?

편집: 이것은 Django REST Framework 업로드 이미지의 복사본일 수 있습니다: "제출된 데이터는 파일이 아닙니다."그것은 아니다.업로드 및 요청은 멀티파트 방식으로 진행되며, 파일 및 업로드도 문제없으니 유의하시기 바랍니다.표준 양식 변수를 사용하여 요청을 완료할 수도 있습니다.대신 JSON payload를 가져올 수 있는지 알고 싶습니다.

파일을 업로드하여 데이터를 전송해야 하는 사용자에게 파일을 바로 사용할 수 있는 방법은 없습니다.이에 대한 json api 사양에 미해결 문제가 있습니다.제가 본 한 가지 가능성은multipart/related여기 보이는 바와 같이 drf로 구현하기는 매우 어렵다고 생각합니다.

마지막으로 제가 실행한 것은 요청서를 보내는 것이었습니다.formdata각 파일을 파일로 전송하고 다른 모든 데이터를 텍스트로 전송합니다.이제 데이터를 텍스트로 전송하기 위해 데이터라는 단일 키를 가지고 전체 json을 값의 문자열로 전송할 수 있습니다.

Models.py

class Posts(models.Model):
    id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    caption = models.TextField(max_length=1000)
    media = models.ImageField(blank=True, default="", upload_to="posts/")
    tags = models.ManyToManyField('Tags', related_name='posts')

serializers.py -> 특별한 변경은 필요 없습니다.쓰기 가능한 ManyToMany Field에 의해 시리얼라이저가 너무 길다고 표시되지 않습니다.

views.py

class PostsViewset(viewsets.ModelViewSet):
    serializer_class = PostsSerializer
    parser_classes = (MultipartJsonParser, parsers.JSONParser)
    queryset = Posts.objects.all()
    lookup_field = 'id'

json을 해석하려면 다음과 같이 커스텀 파서가 필요합니다.

utils.py

from django.http import QueryDict
import json
from rest_framework import parsers

class MultipartJsonParser(parsers.MultiPartParser):

    def parse(self, stream, media_type=None, parser_context=None):
        result = super().parse(
            stream,
            media_type=media_type,
            parser_context=parser_context
        )
        data = {}
        # find the data field and parse it
        data = json.loads(result.data["data"])
        qdict = QueryDict('', mutable=True)
        qdict.update(data)
        return parsers.DataAndFiles(qdict, result.files)

우체부의 요청 예

편집:

각 데이터를 키 값 쌍으로 전송하려면 이 확장 답변을 참조하십시오.

이게 오래된 이야기라는 건 알지만, 우연히 이걸 발견했어요. 수 ★★★★★★★★★★★★★★★★★★★★★★★.MultiPartParser을 사용하다

# views.py
class FileUploadView(views.APIView):
    parser_classes = (MultiPartParser,)

    def put(self, request, filename, format=None):
        file_obj = request.data['file']
        ftype    = request.data['ftype']
        caption  = request.data['caption']
        # ...
        # do some stuff with uploaded file
        # ...
        return Response(status=204)

JS를 사용한 My Angular 코드ng-file-upload 말합니다

file.upload = Upload.upload({
  url: "/api/picture/upload/" + file.name,
  data: {
    file: file,
    ftype: 'final',
    caption: 'This is an image caption'
  }
});

JSON과 제품 오브젝트 작성/업데이트 이미지를 보냅니다.아래는 나에게 적합한 APIView 생성입니다.

시리얼라이저

class ProductCreateSerializer(serializers.ModelSerializer):
    class Meta:
         model = Product
        fields = [
            "id",
            "product_name",
            "product_description",
            "product_price",
          ]
    def create(self,validated_data):
         return Product.objects.create(**validated_data)

보다

from rest_framework  import generics,status
from rest_framework.parsers import FormParser,MultiPartParser

class ProductCreateAPIView(generics.CreateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductCreateSerializer
    permission_classes = [IsAdminOrIsSelf,]
    parser_classes = (MultiPartParser,FormParser,)

    def perform_create(self,serializer,format=None):
        owner = self.request.user
        if self.request.data.get('image') is not None:
            product_image = self.request.data.get('image')
            serializer.save(owner=owner,product_image=product_image)
        else:
            serializer.save(owner=owner)

테스트 예:

def test_product_creation_with_image(self):
    url = reverse('products_create_api')
    self.client.login(username='testaccount',password='testaccount')
    data = {
        "product_name" : "Potatoes",
        "product_description" : "Amazing Potatoes",
        "image" : open("local-filename.jpg","rb")
    }
    response = self.client.post(url,data)
    self.assertEqual(response.status_code,status.HTTP_201_CREATED)

@으로는 문자열로서 @Nithin JSON을 하지 않는 application/json멀티파트 세그먼트 내부에 있습니다.

우리가 원하는 것은 백엔드가 아래 형식의 데이터를 받아들이도록 하는 것이다.

------WebKitFormBoundaryrga771iuUYap8BB2
Content-Disposition: form-data; name="file"; filename="1x1_noexif.jpeg"
Content-Type: image/jpeg


------WebKitFormBoundaryrga771iuUYap8BB2
Content-Disposition: form-data; name="myjson"; filename="blob"
Content-Type: application/json

{"hello":"world"}
------WebKitFormBoundaryrga771iuUYap8BB2
Content-Disposition: form-data; name="isDownscaled"; filename="blob"
Content-Type: application/json

false
------WebKitFormBoundaryrga771iuUYap8BB2--

MultiPartParser는 위의 형식으로 동작하지만, 이러한 jsons는 파일로 취급됩니다.는 그 를 unmarshaling으로 을 해제하면 됩니다.data.

파서화이

from rest_framework import parsers

class MultiPartJSONParser(parsers.MultiPartParser):
    def parse(self, stream, *args, **kwargs):
        data = super().parse(stream, *args, **kwargs)

        # Any 'File' found having application/json as type will be moved to data
        mutable_data = data.data.copy()
        unmarshaled_blob_names = []
        json_parser = parsers.JSONParser()
        for name, blob in data.files.items():
            if blob.content_type == 'application/json' and name not in data.data:
                mutable_data[name] = json_parser.parse(blob)
                unmarshaled_blob_names.append(name)
        for name in unmarshaled_blob_names:
            del data.files[name]
        data.data = mutable_data

        return data

설정을 지정합니다.화이

REST_FRAMEWORK = {
    ..
    'DEFAULT_PARSER_CLASSES': [
        ..
        'myproject.parsers.MultiPartJSONParser',
    ],
}

이제 작동해야 합니다.

이치노 이후client장고 REST는 JSON의 JSON입니다.

import io
import json

def JsonBlob(obj):
    stringified = json.dumps(obj)
    blob = io.StringIO(stringified)
    blob.content_type = 'application/json'
    return blob

def test_simple(client, png_3x3):
    response = client.post(f'http://localhost/files/', {
            'file': png_3x3,
            'metadata': JsonBlob({'lens': 'Sigma 35mm'}),
        }, format='multipart')
    assert response.status_code == 200

Incorrect type. Expected pk value, received list.@@nithin이 있기 QueryDict방해가 되고 있습니다. 사전의 각 항목에 대해 목록을 사용하도록 특별히 구성되어 있으므로 다음과 같습니다.

{ "list": [1, 2] }

에 의해 되는 경우MultipartJsonParser

{ 'list': [[1, 2]] }

시리얼라이저가 고장나죠.

은 이으로, '예상하다'를 들 수 있습니다. 특히 이 문제는_dataJSON © : :

from rest_framework import parsers
import json

class MultiPartJSONParser(parsers.MultiPartParser):
    def parse(self, stream, *args, **kwargs):
        data = super().parse(stream, *args, **kwargs)
        json_data_field = data.data.get('_data')
        if json_data_field is not None:
            parsed = json.loads(json_data_field)
            mutable_data = {}
            for key, value in parsed.items():
                mutable_data[key] = value
            mutable_files = {}
            for key, value in data.files.items():
                if key != '_data':
                    mutable_files[key] = value
            return parsers.DataAndFiles(mutable_data, mutable_files)

        json_data_file = data.files.get('_data')
        if json_data_file:
            parsed = parsers.JSONParser().parse(json_data_file)
            mutable_data = {}
            for key, value in parsed.items():
                mutable_data[key] = value
            mutable_files = {}
            for key, value in data.files.items():
                mutable_files[key] = value
            return parsers.DataAndFiles(mutable_data, mutable_files)

        return data

멀티파트 투고와 일반 뷰를 사용하는 것은 매우 간단합니다(옵션인 경우).

json을 필드로 보내고 파일을 파일로 보낸 다음 하나의 보기에서 처리합니다.

다음은 간단한 python 클라이언트와 Django 서버입니다.

클라이언트 - 여러 파일과 임의 json 인코딩 개체 전송:

import json
import requests

payload = {
    "field1": 1,
    "manifest": "special cakes",
    "nested": {"arbitrary":1, "object":[1,2,3]},
    "hello": "word" }

filenames = ["file1","file2"]
request_files = {}
url="example.com/upload"

for filename in filenames:
    request_files[filename] = open(filename, 'rb')

r = requests.post(url, data={'json':json.dumps(payload)}, files=request_files)

서버 - json을 사용하고 파일을 저장합니다.

@csrf_exempt
def upload(request):
    if request.method == 'POST':
        data = json.loads(request.POST['json']) 
        try:
            manifest = data['manifest']
            #process the json data

        except KeyError:
            HttpResponseServerError("Malformed data!")

        dir = os.path.join(settings.MEDIA_ROOT, "uploads")
        os.makedirs(dir, exist_ok=True)

        for file in request.FILES:
            path = os.path.join(dir,file)
            if not os.path.exists(path):
                save_uploaded_file(path, request.FILES[file])           

    else:
        return HttpResponseNotFound()

    return HttpResponse("Got json data")


def save_uploaded_file(path,f):
    with open(path, 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

@ 것은 @Pithios의에서 DRF가 어떻게 입니다.utils/html#parse_html_list

class MultiPartJSONParser(parsers.MultiPartParser):
    def parse(self, stream, *args, **kwargs):
        data = super().parse(stream, *args, **kwargs)

        # Any 'File' found having application/json as type will be moved to data
        mutable_data = data.data.copy()
        unmarshaled_blob_names = []
        json_parser = parsers.JSONParser()
        for name, blob in data.files.items():
            if blob.content_type == 'application/json' and name not in data.data:
                parsed = json_parser.parse(blob)
                if isinstance(parsed, list):
                    # need to break it out into [0], [1] etc
                    for idx, item in enumerate(parsed):
                        mutable_data[name+f"[{str(idx)}]"] = item
                else:
                    mutable_data[name] = parsed
                unmarshaled_blob_names.append(name)
        for name in unmarshaled_blob_names:
            del data.files[name]
        data.data = mutable_data

        return data

다음 코드가 나에게 효과가 있었다.

from django.core.files.uploadedfile import SimpleUploadedFile
import requests
from typing import Dict

with open(file_path, 'rb') as f:
    file = SimpleUploadedFile('Your-Name', f.read())

    data: Dict[str,str]
    files: Dict[str,SimpleUploadedFile] = {'model_field_name': file}

    requests.put(url, headers=headers, data=data, files=files)
    requests.post(url, headers=headers, data=data, files=files)

'model_field_name'이에요.FileField ★★★★★★★★★★★★★★★★★」ImageField 자료들은 '다보다'요.name ★★★★★★★★★★★★★★★★★」location처럼 '아예'를 data파라미터를 지정합니다.

이게 도움이 됐으면 좋겠다.

이 작업은 다음과 같습니다.

class FileUpload(APIView):
    parser_classes = [MultiPartParser]
    authentication_classes = [JWTAuthentication]

    def post(self, request, filename, format=None):
       file = request.data['file']
       data = json.loads(request.POST['form'])
       #.... just do.....

. . .

프런트 엔드 부품: fetch를 사용한 예(vue 프런트 엔드)

let data = await new FormData(); // creates a new FormData object
data.append("file", this.files); // add your file to form data
data.append('form',JSON.stringify(body)) //add your json

fetch(`https://endpoint/FileUpload/${body.nombre}`, {
  method: 'POST',
  body: data, 
  headers: {Authorization: `Bearer ${accessToken}`}
              })

이게 도움이 됐으면 좋겠어요.

언급URL : https://stackoverflow.com/questions/30176570/using-django-rest-framework-how-can-i-upload-a-file-and-send-a-json-payload

반응형