Code

Ticket #1484: 1484.trunk.2.diff

File 1484.trunk.2.diff, 5.9 KB (added by Maniac <Maniac@…>, 8 years ago)

More correct patch for trunk

Line 
1Index: django/utils/httpwrappers.py
2===================================================================
3--- django/utils/httpwrappers.py        (revision 2509)
4+++ django/utils/httpwrappers.py        (working copy)
5@@ -2,6 +2,8 @@
6 from pprint import pformat
7 from urllib import urlencode
8 from django.utils.datastructures import MultiValueDict
9+import cgi
10+from StringIO import StringIO
11 
12 try:
13     # The mod_python version is more efficient, so try importing it first.
14@@ -32,35 +34,45 @@
15     def get_full_path(self):
16         return ''
17 
18-def parse_file_upload(header_dict, post_data):
19+class FileDict(dict):
20+    "Keeps uploaded file as file-like object and reads its content on demand"
21+    def __getitem__(self, name):
22+        if name=='content' and not 'content' in self:
23+            self['file'].seek(0, 2)
24+            size = self['file'].tell()
25+            self['file'].seek(0, 0)
26+            self['content']=self['file'].read(size)
27+        return dict.__getitem__(self, name)
28+
29+class FieldStorage(cgi.FieldStorage):
30+    "cgi.FieldStorage with ability to store files on disk or in memory"
31+    def make_file(self, binary=None):
32+        from django.conf.settings import STORE_UPLOAD_ON_DISK
33+        if STORE_UPLOAD_ON_DISK:
34+            return cgi.FieldStorage.make_file(self, binary)
35+        else:
36+            return StringIO()
37+
38+def parse_file_upload(post_stream, environ):
39     "Returns a tuple of (POST MultiValueDict, FILES MultiValueDict)"
40-    import email, email.Message
41-    from cgi import parse_header
42-    raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()])
43-    raw_message += '\r\n\r\n' + post_data
44-    msg = email.message_from_string(raw_message)
45+    fs = FieldStorage(post_stream, environ=environ)
46     POST = MultiValueDict()
47     FILES = MultiValueDict()
48-    for submessage in msg.get_payload():
49-        if isinstance(submessage, email.Message.Message):
50-            name_dict = parse_header(submessage['Content-Disposition'])[1]
51-            # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads
52-            # or {'name': 'blah'} for POST fields
53-            # We assume all uploaded files have a 'filename' set.
54-            if name_dict.has_key('filename'):
55-                assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported"
56-                if not name_dict['filename'].strip():
57+    for key in fs.keys():
58+        for value in fs.getlist(key):
59+            if hasattr(fs[key], 'filename') and fs[key].filename is not None:
60+                if not fs[key].filename.strip():
61                     continue
62                 # IE submits the full path, so trim everything but the basename.
63                 # (We can't use os.path.basename because it expects Linux paths.)
64-                filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:]
65-                FILES.appendlist(name_dict['name'], {
66+                filename = fs[key].filename[fs[key].filename.rfind("\\")+1:]
67+                FILES.appendlist(fs[key].name, FileDict({
68                     'filename': filename,
69-                    'content-type': (submessage.has_key('Content-Type') and submessage['Content-Type'] or None),
70-                    'content': submessage.get_payload(),
71-                })
72+                    'content-type': fs[key].type,
73+                    'file': fs[key].file,
74+                }))
75             else:
76-                POST.appendlist(name_dict['name'], submessage.get_payload())
77+                POST.appendlist(fs[key].name, value)
78     return POST, FILES
79 
80 class QueryDict(MultiValueDict):
81Index: django/conf/global_settings.py
82===================================================================
83--- django/conf/global_settings.py      (revision 2509)
84+++ django/conf/global_settings.py      (working copy)
85@@ -194,6 +194,10 @@
86 # http://psyco.sourceforge.net/
87 ENABLE_PSYCO = False
88 
89+# Whether to store uploaded files in temp files rather than in memory.
90+# Storing files on disk may be necessary for accepting large files.
91+STORE_UPLOAD_ON_DISK = False
92+
93 ##############
94 # MIDDLEWARE #
95 ##############
96Index: django/core/handlers/wsgi.py
97===================================================================
98--- django/core/handlers/wsgi.py        (revision 2509)
99+++ django/core/handlers/wsgi.py        (working copy)
100@@ -66,9 +66,7 @@
101         # Populates self._post and self._files
102         if self.environ['REQUEST_METHOD'] == 'POST':
103             if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):
104-                header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')])
105-                header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '')
106-                self._post, self._files = httpwrappers.parse_file_upload(header_dict, self.raw_post_data)
107+                self._post, self._files = httpwrappers.parse_file_upload(self.environ['wsgi.input'], self.environ)
108             else:
109                 self._post, self._files = httpwrappers.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
110         else:
111Index: django/core/handlers/modpython.py
112===================================================================
113--- django/core/handlers/modpython.py   (revision 2509)
114+++ django/core/handlers/modpython.py   (working copy)
115@@ -23,7 +23,10 @@
116     def _load_post_and_files(self):
117         "Populates self._post and self._files"
118         if self._req.headers_in.has_key('content-type') and self._req.headers_in['content-type'].startswith('multipart'):
119-            self._post, self._files = httpwrappers.parse_file_upload(self._req.headers_in, self.raw_post_data)
120+            environ = dict(self.META)
121+            environ['CONTENT_LENGTH'] = environ['HTTP_CONTENT_LENGTH']
122+            environ['CONTENT_TYPE'] = environ['HTTP_CONTENT_TYPE']
123+            self._post, self._files = httpwrappers.parse_file_upload(self._req, environ)
124         else:
125             self._post, self._files = httpwrappers.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
126