【问题标题】:Django Rest Framework File UploadDjango Rest 框架文件上传
【发布时间】:2013-12-26 17:40:25
【问题描述】:

我正在使用 Django Rest Framework 和 AngularJs 上传文件。我的视图文件如下所示:

class ProductList(APIView):
    authentication_classes = (authentication.TokenAuthentication,)
    def get(self,request):
        if request.user.is_authenticated(): 
            userCompanyId = request.user.get_profile().companyId
            products = Product.objects.filter(company = userCompanyId)
            serializer = ProductSerializer(products,many=True)
            return Response(serializer.data)

    def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(data=request.DATA)

由于post方法的最后一行应该返回所有数据,所以我有几个问题:

  • 如何查看request.FILES中是否有内容?
  • 如何序列化文件字段?
  • 我应该如何使用解析器?

【问题讨论】:

  • 只是对 MODS 的说明:自 2013 年以来,Django 已经有了很大的升级。因此,如果其他人现在发布相同的问题。请不要击落他们^_^。
  • Base64 怎么样?
  • Base64 不是一个好的选择,如果你想要一个最好的和几乎完美的应用程序@HojatModaresi

标签: python django angularjs django-rest-framework


【解决方案1】:

使用FileUploadParser,全部在请求中。 改用 put 方法,您会在文档中找到一个示例 :)

class FileUploadView(views.APIView):
    parser_classes = (FileUploadParser,)

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

【讨论】:

  • @pleasedontbelong 为什么这里使用 PUT 方法而不是 POST?
  • 嗨@pleasedontbelong,如果它正在创建新记录,它会改为POST吗?它仍然可以与 FileUploadParser 一起使用吗?
  • @pleasedontbelong RTan 提出了一个很好的问题。阅读 RFC-2616 提供了一个我直到现在才意识到的微妙之处。 “POST 和 PUT 请求之间的根本区别反映在 Request-URI 的不同含义上。POST 请求中的 URI 标识将处理封闭实体的资源。该资源可能是一个数据接受进程,一个网关到某个其他协议或接受注释的单独实体。相反,PUT 请求中的 URI 标识请求中包含的实体"
  • 为什么选择 FileUploadParser? “FileUploadParser 用于可以将文件作为原始数据请求上传的本机客户端。对于基于 Web 的上传或支持分段上传的本机客户端,您应该改用 MultiPartParser 解析器。”一般来说,这似乎不是一个好的选择。更重要的是,我没有看到需要任何特定 treatment 的文件上传。
  • 第二个@x-yuri,当我使用 FileUploadParser 时,DRF 抱怨 Content-Disposition 标头为空。 MultiPartParser 要简单得多,因为它只是假定文件名是表单字段中的给定文件名。
【解决方案2】:

我正在使用相同的堆栈,并且还在寻找文件上传的示例,但我的情况更简单,因为我使用的是 ModelViewSet 而不是 APIView。关键是 pre_save 钩子。我最终将它与 angular-file-upload 模块一起使用,如下所示:

# Django
class ExperimentViewSet(ModelViewSet):
    queryset = Experiment.objects.all()
    serializer_class = ExperimentSerializer

    def pre_save(self, obj):
        obj.samplesheet = self.request.FILES.get('file')

class Experiment(Model):
    notes = TextField(blank=True)
    samplesheet = FileField(blank=True, default='')
    user = ForeignKey(User, related_name='experiments')

class ExperimentSerializer(ModelSerializer):
    class Meta:
        model = Experiment
        fields = ('id', 'notes', 'samplesheet', 'user')

// AngularJS
controller('UploadExperimentCtrl', function($scope, $upload) {
    $scope.submit = function(files, exp) {
        $upload.upload({
            url: '/api/experiments/' + exp.id + '/',
            method: 'PUT',
            data: {user: exp.user.id},
            file: files[0]
        });
    };
});

【讨论】:

【解决方案3】:

我终于可以使用 Django 上传图片了。这是我的工作代码

views.py

class FileUploadView(APIView):
    parser_classes = (FileUploadParser, )

    def post(self, request, format='jpg'):
        up_file = request.FILES['file']
        destination = open('/Users/Username/' + up_file.name, 'wb+')
        for chunk in up_file.chunks():
            destination.write(chunk)
        destination.close()  # File should be closed only after all chuns are added

        # ...
        # do some stuff with uploaded file
        # ...
        return Response(up_file.name, status.HTTP_201_CREATED)

urls.py

urlpatterns = patterns('', 
url(r'^imageUpload', views.FileUploadView.as_view())

curl 请求上传

curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload

【讨论】:

  • 为什么destination.close()放在for循环里面?
  • 似乎最好使用with open('/Users/Username/' + up_file.name, 'wb+') as destination: 并完全删除关闭
  • 使用ModelViewSet更简单。此外,他们很可能实施得更好。
  • 我整天都在依赖这个应答器...直到我发现当你要上传多个文件时,需要的不是FileUploadParser,而是MultiPartParser
【解决方案4】:

我用 ModelViewSet 和 ModelSerializer 解决了这个问题。希望这对社区有所帮助。

我也更喜欢在序列化程序本身而不是视图中进行验证和 Object->JSON(反之亦然)登录。

让我们通过例子来理解它。

说,我想创建 FileUploader API。它将在数据库中存储 id、file_path、file_name、size、owner 等字段。请参阅下面的示例模型:

class FileUploader(models.Model):
    file = models.FileField()
    name = models.CharField(max_length=100) #name is filename without extension
    version = models.IntegerField(default=0)
    upload_date = models.DateTimeField(auto_now=True, db_index=True)
    owner = models.ForeignKey('auth.User', related_name='uploaded_files')
    size = models.IntegerField(default=0)

现在,对于 API,这就是我想要的:

  1. 获取:

当我触发 GET 端点时,我想要每个上传文件的所有上述字段。

  1. 发布:

但是对于用户创建/上传文件,为什么她必须担心传递所有这些字段。她可以只上传文件,然后,我想,序列化程序可以从上传的文件中获取其余字段。

封印器: 问题: 我在下面创建了序列化程序来达到我的目的。但不确定它是否是正确的实现方式。

class FileUploaderSerializer(serializers.ModelSerializer):
    # overwrite = serializers.BooleanField()
    class Meta:
        model = FileUploader
        fields = ('file','name','version','upload_date', 'size')
        read_only_fields = ('name','version','owner','upload_date', 'size')

   def validate(self, validated_data):
        validated_data['owner'] = self.context['request'].user
        validated_data['name'] = os.path.splitext(validated_data['file'].name)[0]
        validated_data['size'] = validated_data['file'].size
        #other validation logic
        return validated_data

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

供参考的视图集:

class FileUploaderViewSet(viewsets.ModelViewSet):
    serializer_class = FileUploaderSerializer
    parser_classes = (MultiPartParser, FormParser,)

    # overriding default query set
    queryset = LayerFile.objects.all()

    def get_queryset(self, *args, **kwargs):
        qs = super(FileUploaderViewSet, self).get_queryset(*args, **kwargs)
        qs = qs.filter(owner=self.request.user)
        return qs

【讨论】:

  • FileUploaderSerializer.validate方法包含哪些验证逻辑?
【解决方案5】:

在 django-rest-framework 请求数据由Parsers解析。
http://www.django-rest-framework.org/api-guide/parsers/

默认情况下,django-rest-framework 采用解析器类JSONParser。它会将数据解析为json。所以,文件不会被它解析。
如果我们希望文件与其他数据一起被解析,我们应该使用以下解析器类之一。

FormParser
MultiPartParser
FileUploadParser

【讨论】:

  • 在当前版本的DRF 3.8.2上,默认解析application/jsonapplication/x-www-form-urlencodedmultipart/form-data
【解决方案6】:

在这上面花了 1 天后,我发现...

对于需要上传文件和发送一些数据的人来说,没有直接的方法可以让它工作。为此,在 json api 规范中有一个 open issue。我见过的一种可能性是使用multipart/related,如here 所示,但我认为在drf 中实现它非常困难。

最后我实现的是将请求发送为formdata。您可以将每个文件作为 file 并将所有其他数据作为文本发送。 现在要以文本形式发送数据,您有两个选择。案例 1)您可以将每个数据作为键值对发送,或者案例 2)您可以有一个名为 data 的键,并将整个 json 作为值中的字符串发送。

如果您有简单的字段,第一种方法可以开箱即用,但如果您有嵌套的序列化,则会出现问题。多部分解析器将无法解析嵌套字段。

下面我提供这两种情况的实现

模型.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 字段实现,这里没有显示我的序列化器,因为它太长了。

views.py

class PostsViewset(viewsets.ModelViewSet):
    serializer_class = PostsSerializer
    #parser_classes = (MultipartJsonParser, parsers.JSONParser) use this if you have simple key value pair as data with no nested serializers
    #parser_classes = (parsers.MultipartParser, parsers.JSONParser) use this if you want to parse json in the key value pair data sent
    queryset = Posts.objects.all()
    lookup_field = 'id'

现在,如果您遵循第一种方法并且仅将非 Json 数据作为键值对发送,则不需要自定义解析器类。 DRF'd MultipartParser 将完成这项工作。但是对于第二种情况,或者如果您有嵌套的序列化程序(如我所展示的),您将需要自定义解析器,如下所示。

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 = {}

        # for case1 with nested serializers
        # parse each field with json
        for key, value in result.data.items():
            if type(value) != str:
                data[key] = value
                continue
            if '{' in value or "[" in value:
                try:
                    data[key] = json.loads(value)
                except ValueError:
                    data[key] = value
            else:
                data[key] = value

        # for case 2
        # 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)

这个序列化器基本上会解析值中的任何 json 内容。

post man 中两种情况的请求示例:case 1 ,

案例2

【讨论】:

  • 我宁愿避免使用情况 2。大多数情况下,每个请求创建一个数据库记录应该没问题。
  • 非常有帮助,非常感谢。但我不明白,为什么要在解析器中将 dict 数据转换为 QueryDict?就我在 Django 中的情况而言,普通字典数据无需转换即可完美运行。
  • 我使用您提到的答案尝试了不同的场景,并且成功运行。你可以看看我的answer
  • 如果这行得通stackoverflow.com/questions/64547729/… 也应该行得通。但它没有。
【解决方案7】:
    from rest_framework import status
    from rest_framework.response import Response
    class FileUpload(APIView):
         def put(request):
             try:
                file = request.FILES['filename']
                #now upload to s3 bucket or your media file
             except Exception as e:
                   print e
                   return Response(status, 
                           status.HTTP_500_INTERNAL_SERVER_ERROR)
             return Response(status, status.HTTP_200_OK)

【讨论】:

    【解决方案8】:
    def post(self,request):
            serializer = ProductSerializer(data=request.DATA, files=request.FILES)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
    

    【讨论】:

    • 这个答案可能需要一些上下文
    【解决方案9】:

    我想写另一个我觉得更干净且更易于维护的选项。我们将使用 defaultRouter 为我们的视图集添加 CRUD url,我们将添加一个固定的 url,指定同一视图集中的上传者视图。

    **** views.py 
    
    from rest_framework import viewsets, serializers
    from rest_framework.decorators import action, parser_classes
    from rest_framework.parsers import JSONParser, MultiPartParser
    from rest_framework.response import Response
    from rest_framework_csv.parsers import CSVParser
    from posts.models import Post
    from posts.serializers import PostSerializer     
    
    
    class PostsViewSet(viewsets.ModelViewSet):
    
        queryset = Post.objects.all()
        serializer_class = PostSerializer 
        parser_classes = (JSONParser, MultiPartParser, CSVParser)
    
    
        @action(detail=False, methods=['put'], name='Uploader View', parser_classes=[CSVParser],)
        def uploader(self, request, filename, format=None):
            # Parsed data will be returned within the request object by accessing 'data' attr  
            _data = request.data
    
            return Response(status=204)
    

    项目的主要 urls.py

    **** urls.py 
    
    from rest_framework import routers
    from posts.views import PostsViewSet
    
    
    router = routers.DefaultRouter()
    router.register(r'posts', PostsViewSet)
    
    urlpatterns = [
        url(r'^posts/uploader/(?P<filename>[^/]+)$', PostsViewSet.as_view({'put': 'uploader'}), name='posts_uploader')
        url(r'^', include(router.urls), name='root-api'),
        url('admin/', admin.site.urls),
    ]
    

    .- 自述文件。

    当我们将@action 装饰器添加到我们的类方法'uploader' 时,神奇的事情发生了。通过指定“methods=['put']”参数,我们只允许 PUT 请求;非常适合文件上传。

    我还添加了参数“parser_classes”以显示您可以选择解析您的内容的解析器。我从 rest_framework_csv 包中添加了 CSVParser,以演示如果需要此功能,我们如何只接受某些类型的文件,在我的情况下,我只接受“Content-Type:text/csv”。 注意:如果您要添加自定义解析器,则需要在 ViewSet 的 parsers_classes 中指定它们,因为请求将在访问上传器方法解析器之前将允许的 media_type 与主(类)解析器进行比较。

    现在我们需要告诉 Django 怎么去这个方法,以及在我们的 url 中哪里可以实现。那是我们添加固定网址的时候(简单目的)。此 Url 将采用稍后将在方法中传递的“文件名”参数。我们需要将这个方法“uploader”传递给 PostsViewSet.as_view 方法,在列表中指定 http 协议 ('PUT')。

    当我们登陆以下网址时

     http://example.com/posts/uploader/ 
    

    它将期望一个带有指定“Content-Type”和 Content-Disposition: 附件的标头的 PUT 请求;文件名="something.csv"。

    curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"
    

    【讨论】:

    • 所以你建议上传一个文件,然后将它附加到一些数据库记录。如果由于某种原因从未发生附加怎么办?为什么不在一个请求中完成呢? parser_classes 不是为了限制可以上传的文件。它让您决定可以使用哪些格式来发出请求。再三考虑,您处理上传的方式......似乎您正在将数据从 CSV 放入数据库。不是 OP 要求的。
    • @x-yuri 说“CSV 是一个文件”,问题是;如何检查请求中是否有数据?通过使用此方法,您将在 request.data 中找到数据。 _data = request.data 由于 PUT 正在使用。就像你说的那样,parser_classes 在那里决定可以使用哪些格式来提出请求,因此通过使用你不想要的任何其他格式,然后将被排除在外,增加了额外的安全层。你如何处理你的数据取决于你。使用“尝试除外”,您可以检查“附加从未发生”是否不需要它,这不是代码的作用。这些是在 1 个请求中完成的
    【解决方案10】:

    根据我的经验,你不需要对文件字段做任何特别的事情,你只需告诉它使用文件字段:

    from rest_framework import routers, serializers, viewsets
    
    class Photo(django.db.models.Model):
        file = django.db.models.ImageField()
    
        def __str__(self):
            return self.file.name
    
    class PhotoSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Photo
            fields = ('id', 'file')   # <-- HERE
    
    class PhotoViewSet(viewsets.ModelViewSet):
        queryset = models.Photo.objects.all()
        serializer_class = PhotoSerializer
    
    router = routers.DefaultRouter()
    router.register(r'photos', PhotoViewSet)
    
    api_urlpatterns = ([
        url('', include(router.urls)),
    ], 'api')
    urlpatterns += [
        url(r'^api/', include(api_urlpatterns)),
    ]
    

    您已准备好上传文件:

    curl -sS http://example.com/api/photos/ -F 'file=@/path/to/file'
    

    为模型具有的每个额外字段添加-F field=value。并且不要忘记添加身份验证。

    【讨论】:

    • 这个。我不确定在所有投票率较高的答案中大惊小怪,也许是在摆弄解析器,而早期版本的 DRF 都是必需的,但截至 2021 年,泛型类仅适用于基本用例,即这些问题似乎主要是关于什么的。
    【解决方案11】:

    这是我应用的一种方法,希望它会有所帮助。

         class Model_File_update(APIView):
             parser_classes = (MultiPartParser, FormParser)
             permission_classes = [IsAuthenticated]  # it will check if the user is authenticated or not
             authentication_classes = [JSONWebTokenAuthentication]  # it will authenticate the person by JSON web token
    
             def put(self, request):
                id = request.GET.get('id')
                obj = Model.objects.get(id=id)
                serializer = Model_Upload_Serializer(obj, data=request.data)
                if serializer.is_valid():
                   serializer.save()
                   return Response(serializer.data, status=200)
                else:
                   return Response(serializer.errors, status=400)
    

    【讨论】:

      【解决方案12】:

      如果有人对 Django Rest Framework 的 ModelViewset 的最简单示例感兴趣。

      模型是,

      class MyModel(models.Model):
          name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
          imageUrl = models.FileField(db_column='image_url', blank=True, null=True, upload_to='images/')
      
          class Meta:
              managed = True
              db_table = 'MyModel'
      

      序列化器,

      class MyModelSerializer(serializers.ModelSerializer):
          class Meta:
              model = MyModel
              fields = "__all__"
      

      视图是,

      class MyModelView(viewsets.ModelViewSet):
          queryset = MyModel.objects.all()
          serializer_class = MyModelSerializer
      

      在 Postman 中测试,

      【讨论】:

      • 我们如何使用 ajax 发送请求。 imageUrl 实际上是什么?
      • imageUrl 是请求中的文件。
      【解决方案13】:

      您可以通过生成解析器类来解析特定字段,然后将这些字段直接馈送到标准 DRF 序列化器中,从而概括@Nithin 的答案以直接使用 DRF 的现有序列化器系统:

      from django.http import QueryDict
      import json
      from rest_framework import parsers
      
      
      def gen_MultipartJsonParser(json_fields):
          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
                  qdict = QueryDict('', mutable=True)
                  for json_field in json_fields:
                      json_data = result.data.get(json_field, None)
                      if not json_data:
                          continue
                      data = json.loads(json_data)
                      if type(data) == list:
                          for d in data:
                              qdict.update({json_field: d})
                      else:
                          qdict.update({json_field: data})
      
                  return parsers.DataAndFiles(qdict, result.files)
      
          return MultipartJsonParser
      

      这样使用:

      class MyFileViewSet(ModelViewSet):
          parser_classes = [gen_MultipartJsonParser(['tags', 'permissions'])]
          #                                           ^^^^^^^^^^^^^^^^^^^
          #                              Fields that need to be further JSON parsed
          ....
      

      【讨论】:

        【解决方案14】:

        如果您使用的是 ModelViewSet,那么实际上您已经完成了!它为您处理所有事情!您只需将字段放入您的 ModelSerializer 并在您的客户端中设置 content-type=multipart/form-data;

        但如您所知,您不能以 json 格式发送文件。 (当您的客户端中的 content-type 设置为 application/json 时)。除非你使用 Base64 格式。

        所以你有两个选择:

        • ModelViewSetModelSerializer 处理作业并使用content-type=multipart/form-data; 发送请求
        • ModelSerializer 中的字段设置为Base64ImageField (or) Base64FileField 并告诉您的客户端将文件编码为Base64 并设置content-type=application/json

        【讨论】:

          【解决方案15】:

          models.py

          from django.db import models
          
          import uuid
          
          class File(models.Model):
              id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
              file = models.FileField(blank=False, null=False)
              
              def __str__(self):
                  return self.file.name
          

          serializers.py

          from rest_framework import serializers
          from .models import File
          
          class FileSerializer(serializers.ModelSerializer):
              class Meta:
                  model = File
                  fields = "__all__"
          

          views.py

          from django.shortcuts import render
          from rest_framework.parsers import FileUploadParser
          from rest_framework.response import Response
          from rest_framework.views import APIView
          from rest_framework import status
          
          from .serializers import FileSerializer
          
          
          class FileUploadView(APIView):
              permission_classes = []
              parser_class = (FileUploadParser,)
          
              def post(self, request, *args, **kwargs):
          
                file_serializer = FileSerializer(data=request.data)
          
                if file_serializer.is_valid():
                    file_serializer.save()
                    return Response(file_serializer.data, status=status.HTTP_201_CREATED)
                else:
                    return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
          

          urls.py

          from apps.files import views as FileViews
          
          urlpatterns = [
              path('api/files', FileViews.FileUploadView.as_view()),
          ]
          

          settings.py

          # file uload parameters
          MEDIA_URL =  '/media/'
          MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
          

          api/files 发送发布请求,并将您的文件附加到form-data 字段file。该文件将被上传到/media 文件夹,并添加一个带有id和文件名的db记录。

          【讨论】:

            【解决方案16】:

            一些解决方案已被弃用(request.data 应该用于 Django 3.0+)。其中一些不验证输入。另外,我希望有一个带有招摇注释的解决方案。所以我推荐使用以下代码:

            from drf_yasg.utils import swagger_auto_schema
            from rest_framework import serializers
            from rest_framework.parsers import MultiPartParser
            from rest_framework.response import Response
            from rest_framework.views import APIView
            
            
            class FileUploadAPI(APIView):
                parser_classes = (MultiPartParser, )
            
                class InputSerializer(serializers.Serializer):
                    image = serializers.ImageField()
            
                @swagger_auto_schema(
                    request_body=InputSerializer
                )
                def put(self, request):
                    input_serializer = self.InputSerializer(data=request.data)
                    input_serializer.is_valid(raise_exception=True)
            
                    # process file
                    file = input_serializer.validated_data['image']
            
                    return Response(status=204)
            
            

            【讨论】:

              【解决方案17】:
              from rest_framework import status, generics
              from rest_framework.response import Response
              from rest_framework import serializers
              import logging
              logger = logging.getLogger(__name__)`enter code here`
              class ImageUploadSerializer(serializers.Serializer):
                  file = serializers.FileField()
              
              class UploadImages(generics.GenericAPIView):
                  
                  serializer_class = ImageUploadSerializer
                  permission_classes = [IsAuthenticated, ]
              
                  def post(self, request):
              
                      try:
                          data = self.serializer_class(data=request.data)
                          if data.is_valid() is False:
                              return Response({'error': ERROR_MESSAGES.get('400')}, status=status.HTTP_400_BAD_REQUEST)
                              is_file_upload_success, file_item = save_aws_article_image(data.validated_data.get('file'),
                                                                                         request.user, upload_type)
              
                          if is_file_upload_success:
                              logger.info('{0} file uploaded {1}'.format(file_item['file_obj'].path, datetime.now()))
                              return Response({'path': file_item['file_obj'].path, 'id': file_item['file_obj'].uuid,
                                               'name': file_item['file_obj'].name},
                                              status=status.HTTP_201_CREATED)
                      except Exception as e:
                          logger.error(e, exc_info=True)
                          return Response({"error": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
              

              【讨论】:

              • 您能在代码中添加解释吗?
              【解决方案18】:

              使用 React(axios) 发送 audioBlob 的 DRF 视图集文件上传示例:

              class MyViewSet(viewsets.ModelViewSet):
                  parser_classes = (MultiPartParser, FormParser)
                  queryset = MyModel.objects.all().order_by('created_at')
                  serializer_class = MySerializer
              

              序列化器:

              class MySerializer(serializers.ModelSerializer):
                  class Meta:
                      model = MyModel
                      fields = '__all__'
              

              型号:

              class MyModel(models.Model):
                  sentence = models.ForeignKey(Sentence, related_name="voice_sentence", on_delete=models.CASCADE)
                  voice_record = models.FileField(blank=True, default='')
                  created_at = models.DateTimeField(auto_now_add=True)
              

              axios:

              export const sendSpeechText = async (audioBlob: any) => {
                  const headers = {
                      'Content-Type': 'application/json',
                      'Content-Disposition': 'attachment; filename=audiofile.webm'
                  }
              
                  const audiofile = new File([audioBlob], "audiofile.webm", { type: "audio/webm" })
              
                  const formData = new FormData();
                  formData.append("sentence", '1');
                  formData.append("voice_record", audiofile);
              
                  return await axios.post(
                      SEND_SPEECH_URL,
                      formData,
                      {
                          crossDomain: true,
                          headers: headers
                      },
                  )
              }
              

              注意:formData 中的 voice_record 在您的模型中应该相同

              【讨论】:

                【解决方案19】:

                我已使用此视图将文件上传到 aws。这里的upload_file 是一个辅助函数,总体而言,您可以使用此视图将文件上传到表单数据中。

                class FileUploadView(GenericAPIView):
                
                    def post(self, request):
                        try:
                            file = request.data['file']
                            if file.content_type == 'image/png' or file.content_type == 'image/jpeg':
                                file_name = upload_file(file)
                                return Response({"name": file_name}, status=status.HTTP_202_ACCEPTED)
                            else:
                                raise UnsupportedMediaType(file.content_type)
                        except KeyError:
                            return Response("file missing.", status=status.HTTP_404_NOT_FOUND)
                

                【讨论】:

                  猜你喜欢
                  • 2015-10-04
                  • 2016-10-25
                  • 2016-02-16
                  • 2021-09-27
                  • 2013-10-25
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多