aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/parsers.py
diff options
context:
space:
mode:
authorTom Christie2013-05-07 05:09:09 -0700
committerTom Christie2013-05-07 05:09:09 -0700
commit642970a1b8e6ebadbbfc9da4d75fad1ec5da6747 (patch)
tree42f0654be36b606f41dbad5259e7b534f0aa350b /rest_framework/parsers.py
parent5356af8651fccacf5524add33569dd84d9e78646 (diff)
parent5faaba9c691851ec68e385cc87d6bce82e4d4853 (diff)
downloaddjango-rest-framework-642970a1b8e6ebadbbfc9da4d75fad1ec5da6747.tar.bz2
Merge pull request #806 from wronglink/master
Added FileUploadParser
Diffstat (limited to 'rest_framework/parsers.py')
-rw-r--r--rest_framework/parsers.py83
1 files changed, 82 insertions, 1 deletions
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index 491acd68..27a0db65 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -6,9 +6,10 @@ on the request, such as form content or json encoded data.
"""
from __future__ import unicode_literals
from django.conf import settings
+from django.core.files.uploadhandler import StopFutureHandlers
from django.http import QueryDict
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
-from django.http.multipartparser import MultiPartParserError
+from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
from rest_framework.compat import yaml, etree
from rest_framework.exceptions import ParseError
from rest_framework.compat import six
@@ -205,3 +206,83 @@ class XMLParser(BaseParser):
pass
return value
+
+
+class FileUploadParser(BaseParser):
+ """
+ Parser for file upload data.
+ """
+ media_type = '*/*'
+
+ def parse(self, stream, media_type=None, parser_context=None):
+ """
+ Returns a DataAndFiles object.
+
+ `.data` will be None (we expect request body to be a file content).
+ `.files` will be a `QueryDict` containing one 'file' elemnt - a parsed file.
+ """
+
+ parser_context = parser_context or {}
+ request = parser_context['request']
+ encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
+ meta = request.META
+ upload_handlers = request.upload_handlers
+ filename = self.get_filename(stream, media_type, parser_context)
+
+ content_type = meta.get('HTTP_CONTENT_TYPE', meta.get('CONTENT_TYPE', ''))
+ try:
+ content_length = int(meta.get('HTTP_CONTENT_LENGTH', meta.get('CONTENT_LENGTH', 0)))
+ except (ValueError, TypeError):
+ content_length = None
+
+ # See if the handler will want to take care of the parsing.
+ for handler in upload_handlers:
+ result = handler.handle_raw_input(None,
+ meta,
+ content_length,
+ None,
+ encoding)
+ if result is not None:
+ return DataAndFiles(None, {'file': result[1]})
+
+ possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size]
+ chunk_size = min([2**31-4] + possible_sizes)
+ chunks = ChunkIter(stream, chunk_size)
+ counters = [0] * len(upload_handlers)
+
+ for handler in upload_handlers:
+ try:
+ handler.new_file(None, filename, content_type, content_length, encoding)
+ except StopFutureHandlers:
+ break
+
+ for chunk in chunks:
+ for i, handler in enumerate(upload_handlers):
+ chunk_length = len(chunk)
+ chunk = handler.receive_data_chunk(chunk, counters[i])
+ counters[i] += chunk_length
+ if chunk is None:
+ # If the chunk received by the handler is None, then don't continue.
+ break
+
+ for i, handler in enumerate(upload_handlers):
+ file_obj = handler.file_complete(counters[i])
+ if file_obj:
+ return DataAndFiles(None, {'file': file_obj})
+ raise ParseError("FileUpload parse error - none of upload handlers can handle the stream")
+
+ def get_filename(self, stream, media_type, parser_context):
+ """
+ Detects the uploaded file name. First searches a 'filename' url kwarg.
+ Then tries to parse Content-Disposition header.
+ """
+ try:
+ return parser_context['kwargs']['filename']
+ except KeyError:
+ pass
+ try:
+ meta = parser_context['request'].META
+ disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'])
+ return disposition[1]['filename']
+ except (AttributeError, KeyError):
+ pass