diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index 9413a1eabb..edf98f6e49 100644 --- a/django/http/multipartparser.py +++ b/django/http/multipartparser.py @@ -199,6 +199,12 @@ 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 + try: chunk = base64.b64decode(chunk) except Exception as e: diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py index 8fa140bc18..45e7342abd 100644 --- a/tests/regressiontests/file_uploads/tests.py +++ b/tests/regressiontests/file_uploads/tests.py @@ -74,15 +74,14 @@ class FileUploadTests(TestCase): self.assertEqual(response.status_code, 200) - def test_base64_upload(self): - test_string = "This data will be transmitted base64-encoded." + def _test_base64_upload(self, content): 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(test_string)) + b"\r\n") + payload.write(b"\r\n" + base64.b64encode(force_bytes(content)) + b"\r\n") payload.write('--' + client.BOUNDARY + '--\r\n') r = { 'CONTENT_LENGTH': len(payload), @@ -94,7 +93,13 @@ class FileUploadTests(TestCase): response = self.client.request(**r) received = json.loads(response.content.decode('utf-8')) - self.assertEqual(received['file'], test_string) + self.assertEqual(received['file'], content) + + def test_base64_upload(self): + self._test_base64_upload("This data will be transmitted base64-encoded.") + + def test_big_base64_upload(self): + self._test_base64_upload("Big data" * 68000) # > 512Kb def test_unicode_file_name(self): tdir = sys_tempfile.mkdtemp()