Upload files from HTML forms directly to the backend.
authorAntony Chazapis <chazapis@gmail.com>
Wed, 23 Nov 2011 10:08:03 +0000 (12:08 +0200)
committerAntony Chazapis <chazapis@gmail.com>
Wed, 23 Nov 2011 10:08:03 +0000 (12:08 +0200)
Fixes #1676

pithos/api/functions.py
pithos/api/util.py

index eb9efd7..b93ff6e 100644 (file)
@@ -48,7 +48,7 @@ from pithos.api.util import (rename_meta_key, format_header_key, printable_heade
     put_account_headers, get_container_headers, put_container_headers, get_object_headers, put_object_headers,
     update_manifest_meta, update_sharing_meta, update_public_meta, validate_modification_preconditions,
     validate_matching_preconditions, split_container_object_string, copy_or_move_object,
-    get_int_parameter, get_content_length, get_content_range, socket_read_iterator,
+    get_int_parameter, get_content_length, get_content_range, socket_read_iterator, SaveToBackendHandler,
     object_data_response, put_object_block, hashmap_hash, api_method, json_encode_decimal)
 from pithos.backends.base import NotAllowedError, QuotaError
 
@@ -863,26 +863,18 @@ def object_write_form(request, v_account, v_container, v_object):
     #                       forbidden (403),
     #                       badRequest (400)
     
+    request.upload_handlers = [SaveToBackendHandler(request)]
     if not request.FILES.has_key('X-Object-Data'):
         raise BadRequest('Missing X-Object-Data field')
     file = request.FILES['X-Object-Data']
     
     meta = {}
     meta['Content-Type'] = file.content_type
-    
-    md5 = hashlib.md5()
-    size = 0
-    hashmap = []
-    for data in file.chunks(request.backend.block_size):
-        size += len(data)
-        hashmap.append(request.backend.put_block(data))
-        md5.update(data)
-    
-    meta['ETag'] = md5.hexdigest().lower()
+    meta['ETag'] = file.etag
     
     try:
         version_id = request.backend.update_object_hashmap(request.user_uniq,
-                    v_account, v_container, v_object, size, hashmap, meta, True)
+                        v_account, v_container, v_object, file.size, file.hashmap, meta, True)
     except NotAllowedError:
         raise Forbidden('Not allowed')
     except NameError:
index ae7490f..ccc83ec 100644 (file)
@@ -43,6 +43,8 @@ from django.http import HttpResponse
 from django.utils import simplejson as json
 from django.utils.http import http_date, parse_etags
 from django.utils.encoding import smart_str
+from django.core.files.uploadhandler import FileUploadHandler
+from django.core.files.uploadedfile import UploadedFile
 
 from pithos.api.compat import parse_http_date_safe, parse_http_date
 from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound,
@@ -556,6 +558,40 @@ def socket_read_iterator(request, length=0, blocksize=4096):
             length -= len(data)
             yield data
 
+class SaveToBackendHandler(FileUploadHandler):
+    """Handle a file from an HTML form the django way."""
+    
+    def __init__(self, request=None):
+        super(SaveToBackendHandler, self).__init__(request)
+        self.backend = request.backend
+    
+    def put_data(self, length):
+        if len(self.data) >= length:
+            block = self.data[:length]
+            self.file.hashmap.append(self.backend.put_block(block))
+            self.md5.update(block)
+            self.data = self.data[length:]
+    
+    def new_file(self, field_name, file_name, content_type, content_length, charset=None):
+        self.md5 = hashlib.md5()        
+        self.data = ''
+        self.file = UploadedFile(name=file_name, content_type=content_type, charset=charset)
+        self.file.size = 0
+        self.file.hashmap = []
+    
+    def receive_data_chunk(self, raw_data, start):
+        self.data += raw_data
+        self.file.size += len(raw_data)
+        self.put_data(self.request.backend.block_size)
+        return None
+    
+    def file_complete(self, file_size):
+        l = len(self.data)
+        if l > 0:
+            self.put_data(l)
+        self.file.etag = self.md5.hexdigest().lower()
+        return self.file
+
 class ObjectWrapper(object):
     """Return the object's data block-per-block in each iteration.