171 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# -*- 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
 | 
						|
)
 |