diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 7d2c056e3..5bd79a317 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -114,6 +114,7 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword * The `FileUploadParser` is for usage with native clients that can upload the file as a raw data request. For web-based uploads, or for native clients with multipart upload support, you should use the `MultiPartParser` parser instead. * Since this parser's `media_type` matches any content type, `FileUploadParser` should generally be the only parser set on an API view. +* `FileUploadParser` respects Django's standard `FILE_UPLOAD_HANDLERS` setting, and the `request.upload_handlers` attribute. See the [Django documentation][upload-handlers] for more details. ##### Basic usage example: @@ -183,6 +184,7 @@ The following third party packages are also available. [jquery-ajax]: http://api.jquery.com/jQuery.ajax/ [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion +[upload-handlers]: https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#upload-handlers [messagepack]: https://github.com/juanriaza/django-rest-framework-msgpack [juanriaza]: https://github.com/juanriaza [djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 614531a16..25be2e6ab 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -219,7 +219,7 @@ class FileUploadParser(BaseParser): 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. + `.files` will be a `QueryDict` containing one 'file' element. """ parser_context = parser_context or {} @@ -229,9 +229,13 @@ class FileUploadParser(BaseParser): 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', '')) + # Note that this code is extracted from Django's handling of + # file uploads in MultiPartParser. + 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))) + content_length = int(meta.get('HTTP_CONTENT_LENGTH', + meta.get('CONTENT_LENGTH', 0))) except (ValueError, TypeError): content_length = None @@ -245,6 +249,7 @@ class FileUploadParser(BaseParser): if result is not None: return DataAndFiles(None, {'file': result[1]}) + # This is the standard case. 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) @@ -252,7 +257,8 @@ class FileUploadParser(BaseParser): for handler in upload_handlers: try: - handler.new_file(None, filename, content_type, content_length, encoding) + handler.new_file(None, filename, content_type, + content_length, encoding) except StopFutureHandlers: break @@ -262,14 +268,14 @@ class FileUploadParser(BaseParser): 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") + raise ParseError("FileUpload parse error - " + "none of upload handlers can handle the stream") def get_filename(self, stream, media_type, parser_context): """ @@ -286,4 +292,4 @@ class FileUploadParser(BaseParser): disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION']) return disposition[1]['filename'] except (AttributeError, KeyError): - raise ParseError("Filename must be set in Content-Disposition header.") + pass