RESTful方式上传文件以及django中的一些数据

时间:2015-05-08 14:49:21

标签: python django rest django-rest-framework hmac

我正在使用django rest framework创建一个使用django的web服务。 用户可以上传一些图像和视频。上传媒体是一个两步操作,第一个用户上传文件并接收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中解析或在用户端创建的数据大小进行一些限制。 (这个web服务应该与Android / iOS应用程序通信,它们的内存有限)!这是一个好的解决方案吗?我担心的是真正的问题,还是我不应该担心?更好的解决方案?

1 个答案:

答案 0 :(得分:1)

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

然后您可以让POST API直接接受该文件并简单地返回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+可能不适用。由于嵌套序列化程序支持的丢失,我没有升级。