From 1d5baece1e1244558882835dacafc1d7c8def8ac Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Tue, 4 Aug 2020 13:42:28 +0300 Subject: [PATCH] Chunk uploading: implement properly using a custom Parser. --- .../migrations/0022_auto_20200804_1059.py | 17 ++++++++++++++ django_etebase/models.py | 3 +++ django_etebase/parsers.py | 15 +++++++++++++ django_etebase/views.py | 22 ++++++++++++++----- 4 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 django_etebase/migrations/0022_auto_20200804_1059.py create mode 100644 django_etebase/parsers.py diff --git a/django_etebase/migrations/0022_auto_20200804_1059.py b/django_etebase/migrations/0022_auto_20200804_1059.py new file mode 100644 index 0000000..c47e562 --- /dev/null +++ b/django_etebase/migrations/0022_auto_20200804_1059.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.3 on 2020-08-04 10:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_etebase', '0021_auto_20200626_0913'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='collectionitemchunk', + unique_together={('item', 'uid')}, + ), + ] diff --git a/django_etebase/models.py b/django_etebase/models.py index f3704a3..0c33301 100644 --- a/django_etebase/models.py +++ b/django_etebase/models.py @@ -100,6 +100,9 @@ class CollectionItemChunk(models.Model): def __str__(self): return self.uid + class Meta: + unique_together = ('item', 'uid') + def generate_stoken_uid(): return get_random_string(32, allowed_chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_') diff --git a/django_etebase/parsers.py b/django_etebase/parsers.py new file mode 100644 index 0000000..1ca1a70 --- /dev/null +++ b/django_etebase/parsers.py @@ -0,0 +1,15 @@ +from rest_framework.parsers import FileUploadParser + + +class ChunkUploadParser(FileUploadParser): + """ + Parser for chunk upload data. + """ + media_type = 'application/octet-stream' + + def get_filename(self, stream, media_type, parser_context): + """ + Detects the uploaded file name. + """ + view = parser_context['view'] + return parser_context['kwargs'][view.lookup_field] diff --git a/django_etebase/views.py b/django_etebase/views.py index eb04e4b..c299dc2 100644 --- a/django_etebase/views.py +++ b/django_etebase/views.py @@ -73,6 +73,7 @@ from .serializers import ( ) from .utils import get_user_queryset from .exceptions import EtebaseValidationError +from .parsers import ChunkUploadParser User = get_user_model() @@ -398,11 +399,11 @@ class CollectionItemViewSet(BaseViewSet): class CollectionItemChunkViewSet(viewsets.ViewSet): - allowed_methods = ['GET', 'POST'] + allowed_methods = ['GET', 'PUT'] authentication_classes = BaseViewSet.authentication_classes permission_classes = BaseViewSet.permission_classes renderer_classes = BaseViewSet.renderer_classes - parser_classes = (MultiPartParser, ) + parser_classes = (ChunkUploadParser, ) serializer_class = CollectionItemChunkSerializer lookup_field = 'uid' @@ -413,13 +414,24 @@ class CollectionItemChunkViewSet(viewsets.ViewSet): user = self.request.user return queryset.filter(members__user=user) - def create(self, request, collection_uid=None, collection_item_uid=None, *args, **kwargs): + def update(self, request, *args, collection_uid=None, collection_item_uid=None, uid=None, **kwargs): col = get_object_or_404(self.get_collection_queryset(), main_item__uid=collection_uid) col_it = get_object_or_404(col.items, uid=collection_item_uid) - serializer = self.get_serializer_class()(data=request.data) + data = { + "uid": uid, + "chunkFile": request.data["file"], + } + + serializer = self.get_serializer_class()(data=data) serializer.is_valid(raise_exception=True) - serializer.save(item=col_it) + try: + serializer.save(item=col_it) + except IntegrityError: + return Response( + {"code": "chunk_exists", "detail": "Chunk already exists."}, + status=status.HTTP_409_CONFLICT + ) return Response({}, status=status.HTTP_201_CREATED)