# -*- coding: utf-8 -*- # # jQuery File Upload Plugin GAE Python Example 2.1.1 # https://github.com/blueimp/jQuery-File-Upload # # Copyright 2011, Sebastian Tschan # https://blueimp.net # # Licensed under the MIT license: # http://www.opensource.org/licenses/MIT # from __future__ import with_statement from google.appengine.api import files, images from google.appengine.ext import blobstore, deferred from google.appengine.ext.webapp import blobstore_handlers import json import re import urllib import webapp2 WEBSITE = 'http://blueimp.github.io/jQuery-File-Upload/' MIN_FILE_SIZE = 1 # bytes MAX_FILE_SIZE = 5000000 # bytes IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)') ACCEPT_FILE_TYPES = IMAGE_TYPES THUMBNAIL_MODIFICATOR = '=s80' # max width / height EXPIRATION_TIME = 300 # seconds def cleanup(blob_keys): blobstore.delete(blob_keys) class UploadHandler(webapp2.RequestHandler): def initialize(self, request, response): super(UploadHandler, self).initialize(request, response) self.response.headers['Access-Control-Allow-Origin'] = '*' self.response.headers[ 'Access-Control-Allow-Methods' ] = 'OPTIONS, HEAD, GET, POST, PUT, DELETE' self.response.headers[ 'Access-Control-Allow-Headers' ] = 'Content-Type, Content-Range, Content-Disposition' def validate(self, file): if file['size'] < MIN_FILE_SIZE: file['error'] = 'File is too small' elif file['size'] > MAX_FILE_SIZE: file['error'] = 'File is too big' elif not ACCEPT_FILE_TYPES.match(file['type']): file['error'] = 'Filetype not allowed' else: return True return False def get_file_size(self, file): file.seek(0, 2) # Seek to the end of the file size = file.tell() # Get the position of EOF file.seek(0) # Reset the file position to the beginning return size def write_blob(self, data, info): blob = files.blobstore.create( mime_type=info['type'], _blobinfo_uploaded_filename=info['name'] ) with files.open(blob, 'a') as f: f.write(data) files.finalize(blob) return files.blobstore.get_blob_key(blob) def handle_upload(self): results = [] blob_keys = [] for name, fieldStorage in self.request.POST.items(): if type(fieldStorage) is unicode: continue result = {} result['name'] = re.sub( r'^.*\\', '', fieldStorage.filename ) result['type'] = fieldStorage.type result['size'] = self.get_file_size(fieldStorage.file) if self.validate(result): blob_key = str( self.write_blob(fieldStorage.value, result) ) blob_keys.append(blob_key) result['deleteType'] = 'DELETE' result['deleteUrl'] = self.request.host_url +\ '/?key=' + urllib.quote(blob_key, '') if (IMAGE_TYPES.match(result['type'])): try: result['url'] = images.get_serving_url( blob_key, secure_url=self.request.host_url.startswith( 'https' ) ) result['thumbnailUrl'] = result['url'] +\ THUMBNAIL_MODIFICATOR except: # Could not get an image serving url pass if not 'url' in result: result['url'] = self.request.host_url +\ '/' + blob_key + '/' + urllib.quote( result['name'].encode('utf-8'), '') results.append(result) deferred.defer( cleanup, blob_keys, _countdown=EXPIRATION_TIME ) return results def options(self): pass def head(self): pass def get(self): self.redirect(WEBSITE) def post(self): if (self.request.get('_method') == 'DELETE'): return self.delete() result = {'files': self.handle_upload()} s = json.dumps(result, separators=(',', ':')) redirect = self.request.get('redirect') if redirect: return self.redirect(str( redirect.replace('%s', urllib.quote(s, ''), 1) )) if 'application/json' in self.request.headers.get('Accept'): self.response.headers['Content-Type'] = 'application/json' self.response.write(s) def delete(self): key = self.request.get('key') or '' blobstore.delete(key) s = json.dumps({key: True}, separators=(',', ':')) if 'application/json' in self.request.headers.get('Accept'): self.response.headers['Content-Type'] = 'application/json' self.response.write(s) class DownloadHandler(blobstore_handlers.BlobstoreDownloadHandler): def get(self, key, filename): if not blobstore.get(key): self.error(404) else: # Prevent browsers from MIME-sniffing the content-type: self.response.headers['X-Content-Type-Options'] = 'nosniff' # Cache for the expiration time: self.response.headers['Cache-Control'] = 'public,max-age=%d' % EXPIRATION_TIME # Send the file forcing a download dialog: self.send_blob(key, save_as=filename, content_type='application/octet-stream') app = webapp2.WSGIApplication( [ ('/', UploadHandler), ('/([^/]+)/([^/]+)', DownloadHandler) ], debug=True )