RESTful 在 Django 中上传文件和一些数据的方法

RESTful way to upload file along with some data in django

我正在使用 django rest framework 使用 django 创建一个网络服务。 用户可以上传一些图片和视频。上传媒体是一个两步操作,首先用户上传文件并收到一个 ID,然后在单独的请求中使用该 ID 来引用媒体(例如,他可以将其用作个人资料图片或在聊天消息中使用它).

我需要知道 正在为 HMAC 身份验证中间件上传媒体并在数据库中设置媒体所有者。所有其他请求均采用 JSON 格式,并包含一个 username 字段,HMAC 中间件使用该字段检索秘密共享密钥。

我首先想到的是媒体上传 api 可能是这样的:

{
  "username":"mjafar",
  "datetime":"2015-05-08 19:05",
  "media_type":"photo",
  "media_data": /* base64 encoded image file */
}

但我认为 base64 编码对于像视频这样的大文件可能会有很大的开销;或者可能对可以在 json 中解析或在用户端创建的数据大小有一些限制。 (此网络服务应该与 Android/iOS 应用程序通信,它们的内存有限)!这是一个好的解决方案吗?我的担忧是真正的问题还是我不​​应该担心?更好的解决方案?

你可以把两者分开。一个界面上的元数据,带有指向实际文件的 URL。根据您存储实际文件的方式,您可以稍后通过 URL 直接引用该文件。

然后您可以让 POST API 直接接受文件并简单地 return JSON 元数据

{
  "username":"mjafar", // inferred from self.request.user
  "datetime":"2015-05-08 19:05", // timestamp on server
  "media_type":"photo", // inferred from header content-type?
  // auto-generated hashed location for file
  "url": "/files/1dde/2ecf/4075/f61b/5a9c/1cec/53e0/ca9b/4b58/c153/09da/f4c1/9e09/4126/271f/fb4e/foo.jpg"
}

使用 DRF 创建这样的界面将更符合实施 rest_framework.views.APIView

这是我正在为我的网站之一做的事情:

class UploadedFile(models.Model):
    creator = models.ForeignKey(auth_models.User,blank=True)
    creation_datetime = models.DateTimeField(blank=True,null=True)
    title = models.CharField(max_length=100)
    file = models.FileField(max_length=200, upload_to=FileSubpath)
    sha256 = models.CharField(max_length=64,db_index=True)
    def save(self,*args,**kw_args):
        if not self.creation_datetime:
            self.creation_datetime = UTC_Now()
        super(UploadedFile,self).save(*args,**kw_args)

序列化器:

class UploadedFileSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = UploadedFile
        fields = ('url', 'creator','creation_datetime','title','file')

以及使用这个的视图:

from rest_framework.views import APIView
from qc_srvr import serializers,models
from rest_framework.response import Response
from rest_framework import status
from rest_framework import parsers
from rest_framework import renderers
import django.contrib.auth.models as auth_models
import hashlib


class UploadFile(APIView):
    '''A page for uploading files.'''

    throttle_classes = ()
    permission_classes = ()
    parser_classes = (parsers.FormParser, parsers.JSONParser,)
    renderer_classes = (renderers.JSONRenderer,)
    serializer_class = serializers.UploadedFileSerializer

    def calc_sha256(self,afile):
        hasher = hashlib.sha256()
        blocksize=65536
        hasher.update('af1f9847d67300b996edce88889e358ab81f658ff71d2a2e60046c2976eeebdb') # salt
        buf = afile.read(blocksize)
        while len(buf) > 0:
            hasher.update(buf)
            buf = afile.read(blocksize)
        return hasher.hexdigest()


    def post(self, request):
        if not request.user.is_authenticated():
            return Response('User is not authenticated.', status=status.HTTP_401_UNAUTHORIZED)

        uploaded_file = request.FILES.get('file',None)
        if not uploaded_file:
            return Response('No upload file was specified.', status=status.HTTP_400_BAD_REQUEST)


        # calculate sha
        sha256 = self.calc_sha256(uploaded_file)  

        # does the file already exist?
        existing_files = models.UploadedFile.objects.filter(sha256=sha256)
        if len(existing_files):
            serializer = self.serializer_class(instance=existing_files[0],context={'request':request})
        else:
            instance = models.UploadedFile.objects.create(
                creator = request.user,
                title= uploaded_file.name,
                file = uploaded_file,
                sha256 = sha256)
            serializer = self.serializer_class(instance=instance,context={'request':request})
        #import rpdb2; rpdb2.start_embedded_debugger('foo')
        #serializer.is_valid()
        return Response(serializer.data)

仅供参考,这有点隐蔽,因为如果您有文件的 URL,所有上传的文件都可以检索。

我仍在使用 DRF 2.4.4,因此这可能不适用于 3+。由于嵌套序列化程序支持被删除,我还没有升级。