|
#!/usr/bin/python |
|
# -*- coding: utf-8 -*- |
|
from __future__ import unicode_literals, with_statement |
|
|
|
import webapp2 |
|
import mimetypes |
|
from google.appengine.ext.ndb import blobstore |
|
from google.appengine.ext.webapp import blobstore_handlers |
|
from google.appengine.api import urlfetch |
|
from google.appengine.runtime import DeadlineExceededError |
|
import logging |
|
|
|
CRLF = b'\r\n' |
|
BOUNDERY = b'--Th15-Is-ThE-BoUnDeRy--' |
|
max_buffer_size = 1048576 |
|
|
|
|
|
def create_upload_url(for_url): |
|
|
|
return blobstore.create_upload_url(for_url, max_bytes_per_blob=max_buffer_size) |
|
|
|
|
|
def _fetch_blob_update(payload): |
|
|
|
upload_url = create_upload_url(webapp2.uri_for('blob_update', _full=True)) |
|
try: |
|
response = urlfetch.fetch( |
|
url=upload_url, |
|
payload=payload, |
|
method=urlfetch.POST, |
|
deadline=40, |
|
follow_redirects=False, |
|
headers={b'Content-Type': str('multipart/form-data; boundary=%s' % BOUNDERY)} |
|
) |
|
if response.status_code != 200: |
|
logging.error('URL : %s fetch ERROR : %d' % (upload_url, response.status_code)) |
|
return None |
|
else: |
|
return response.content |
|
except (urlfetch.DownloadError, DeadlineExceededError), e: |
|
logging.error('fetch %s download or deadline error, exception : %s' |
|
% (upload_url, str(e))) |
|
return None |
|
|
|
|
|
def _encode_multipart(_fields=None, _files=None): |
|
""" Generator inserts fields and files to create a multipart payload """ |
|
|
|
for (field_name, field_value) in _fields or []: |
|
yield b'--' + BOUNDERY + CRLF |
|
yield b'Content-Disposition: form-data; name="%s"' % str(field_name) + CRLF |
|
yield CRLF |
|
yield str(field_value) + CRLF |
|
|
|
for (file_name, file_data) in _files or []: |
|
yield b'--' + BOUNDERY + CRLF |
|
yield b'Content-Disposition: form-data; name="file"; filename="%s"' \ |
|
% str(file_name) + CRLF |
|
yield b'Content-Type: %s' % mimetypes.guess_type(file_name)[0] + CRLF |
|
yield CRLF |
|
yield file_data + CRLF |
|
|
|
yield b'--%s--' % BOUNDERY + CRLF |
|
|
|
|
|
def blob_update_by_upload(ndb_key, blob): |
|
""" update blob using blobstore upload """ |
|
|
|
result = _fetch_blob_update(b''.join(_encode_multipart(_files=[(ndb_key.id(), blob)]))) |
|
return blobstore.BlobKey(result) |
|
|
|
|
|
class BlobUpdate(blobstore_handlers.BlobstoreUploadHandler): |
|
""" has named route : blob_update : |
|
webapp2.Route(r'/blob_update', handler='....BlobUpdate', name='blob_update') """ |
|
|
|
def post(self): |
|
""" blob upload handler returns the new blobkey""" |
|
|
|
blob_info = self.get_uploads('file')[0] |
|
logging.info('BlobUpdate : ' + blob_info.filename) |
|
self.response.headers[b'Content-Type'] = b'text/plain' |
|
self.response.write(str(blob_info.key())) |