diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index fd9f3a5b32..07c874e08f 100644 --- a/django/http/multipartparser.py +++ b/django/http/multipartparser.py @@ -206,14 +206,19 @@ class MultiPartParser(object): for chunk in field_stream: if transfer_encoding == 'base64': # We only special-case base64 transfer encoding - # We should always read base64 streams by multiple of 4 - over_bytes = len(chunk) % 4 - if over_bytes: - over_chunk = field_stream.read(4 - over_bytes) - chunk += over_chunk + # We should always decode base64 chunks by multiple of 4, + # ignoring whitespace. + + stripped_chunk = b"".join(chunk.split()) + + remaining = len(stripped_chunk) % 4 + while remaining != 0: + over_chunk = field_stream.read(4 - remaining) + stripped_chunk += b"".join(over_chunk.split()) + remaining = len(stripped_chunk) % 4 try: - chunk = base64.b64decode(chunk) + chunk = base64.b64decode(stripped_chunk) except Exception as e: # Since this is only a chunk, any error is an unfixable error. msg = "Could not decode base64 data: %r" % e diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py index 39f4caf6cd..66d055e8cf 100644 --- a/tests/file_uploads/tests.py +++ b/tests/file_uploads/tests.py @@ -77,14 +77,14 @@ class FileUploadTests(TestCase): self.assertEqual(response.status_code, 200) - def _test_base64_upload(self, content): + def _test_base64_upload(self, content, encode=base64.b64encode): payload = client.FakePayload("\r\n".join([ '--' + client.BOUNDARY, 'Content-Disposition: form-data; name="file"; filename="test.txt"', 'Content-Type: application/octet-stream', 'Content-Transfer-Encoding: base64', ''])) - payload.write(b"\r\n" + base64.b64encode(force_bytes(content)) + b"\r\n") + payload.write(b"\r\n" + encode(force_bytes(content)) + b"\r\n") payload.write('--' + client.BOUNDARY + '--\r\n') r = { 'CONTENT_LENGTH': len(payload), @@ -104,6 +104,10 @@ class FileUploadTests(TestCase): def test_big_base64_upload(self): self._test_base64_upload("Big data" * 68000) # > 512Kb + def test_big_base64_newlines_upload(self): + self._test_base64_upload( + "Big data" * 68000, encode=base64.encodestring) + def test_unicode_file_name(self): tdir = sys_tempfile.mkdtemp() self.addCleanup(shutil.rmtree, tdir, True)