| Index: appengine/monorail/framework/gcs_helpers.py
|
| diff --git a/appengine/monorail/framework/gcs_helpers.py b/appengine/monorail/framework/gcs_helpers.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..843eac94aca936dda12b9316133cd16ec5d60f62
|
| --- /dev/null
|
| +++ b/appengine/monorail/framework/gcs_helpers.py
|
| @@ -0,0 +1,118 @@
|
| +# Copyright 2016 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is govered by a BSD-style
|
| +# license that can be found in the LICENSE file or at
|
| +# https://developers.google.com/open-source/licenses/bsd
|
| +
|
| +"""Set of helpers for interacting with Google Cloud Storage."""
|
| +
|
| +import base64
|
| +import logging
|
| +import os
|
| +import time
|
| +import urllib
|
| +import uuid
|
| +
|
| +from datetime import datetime, timedelta
|
| +
|
| +from google.appengine.api import app_identity
|
| +from google.appengine.api import images
|
| +from third_party import cloudstorage
|
| +
|
| +from framework import filecontent
|
| +
|
| +
|
| +ATTACHMENT_TTL = timedelta(seconds=30)
|
| +
|
| +IS_DEV_APPSERVER = (
|
| + 'development' in os.environ.get('SERVER_SOFTWARE', '').lower())
|
| +
|
| +RESIZABLE_MIME_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif']
|
| +
|
| +DEFAULT_THUMB_WIDTH = 250
|
| +DEFAULT_THUMB_HEIGHT = 200
|
| +LOGO_THUMB_WIDTH = 110
|
| +LOGO_THUMB_HEIGHT = 30
|
| +
|
| +
|
| +def _Now():
|
| + return datetime.utcnow()
|
| +
|
| +
|
| +class UnsupportedMimeType(Exception):
|
| + pass
|
| +
|
| +
|
| +def DeleteObjectFromGCS(object_id):
|
| + object_path = ('/' + app_identity.get_default_gcs_bucket_name() + object_id)
|
| + cloudstorage.delete(object_path)
|
| +
|
| +
|
| +def StoreObjectInGCS(
|
| + content, mime_type, project_id, thumb_width=DEFAULT_THUMB_WIDTH,
|
| + thumb_height=DEFAULT_THUMB_HEIGHT):
|
| + bucket_name = app_identity.get_default_gcs_bucket_name()
|
| + guid = uuid.uuid4()
|
| + object_id = '/%s/attachments/%s' % (project_id, guid)
|
| + object_path = '/' + bucket_name + object_id
|
| + with cloudstorage.open(object_path, 'w', mime_type) as f:
|
| + f.write(content)
|
| +
|
| + if mime_type in RESIZABLE_MIME_TYPES:
|
| + # Create and save a thumbnail too.
|
| + thumb_content = None
|
| + try:
|
| + thumb_content = images.resize(content, thumb_width, thumb_height)
|
| + except Exception, e:
|
| + # Do not raise exception for incorrectly formed images.
|
| + # See https://bugs.chromium.org/p/monorail/issues/detail?id=597 for more
|
| + # detail.
|
| + logging.exception(e)
|
| + if thumb_content:
|
| + thumb_path = '%s-thumbnail' % object_path
|
| + with cloudstorage.open(thumb_path, 'w', 'image/png') as f:
|
| + f.write(thumb_content)
|
| +
|
| + return object_id
|
| +
|
| +
|
| +def CheckMimeTypeResizable(mime_type):
|
| + if mime_type not in RESIZABLE_MIME_TYPES:
|
| + raise UnsupportedMimeType(
|
| + 'Please upload a logo with one of the following mime types:\n%s' %
|
| + ', '.join(RESIZABLE_MIME_TYPES))
|
| +
|
| +
|
| +def StoreLogoInGCS(file_name, content, project_id):
|
| + mime_type = filecontent.GuessContentTypeFromFilename(file_name)
|
| + CheckMimeTypeResizable(mime_type)
|
| + if '\\' in file_name: # IE insists on giving us the whole path.
|
| + file_name = file_name[file_name.rindex('\\') + 1:]
|
| + return StoreObjectInGCS(
|
| + content, mime_type, project_id, thumb_width=LOGO_THUMB_WIDTH,
|
| + thumb_height=LOGO_THUMB_HEIGHT)
|
| +
|
| +
|
| +def SignUrl(gcs_filename):
|
| + expiration_dt = _Now() + ATTACHMENT_TTL
|
| + expiration = int(time.mktime(expiration_dt.timetuple()))
|
| + signature_string = '\n'.join([
|
| + 'GET',
|
| + '', # Optional MD5, which we don't have.
|
| + '', # Optional content-type, which only applies to uploads.
|
| + str(expiration),
|
| + gcs_filename]).encode('utf-8')
|
| +
|
| + signature_bytes = app_identity.sign_blob(signature_string)[1]
|
| +
|
| + query_params = {'GoogleAccessId': app_identity.get_service_account_name(),
|
| + 'Expires': str(expiration),
|
| + 'Signature': base64.b64encode(signature_bytes)}
|
| +
|
| + result = 'https://storage.googleapis.com{resource}?{querystring}'
|
| +
|
| + if IS_DEV_APPSERVER:
|
| + result = '/_ah/gcs{resource}?{querystring}'
|
| +
|
| + return result.format(
|
| + resource=gcs_filename, querystring=urllib.urlencode(query_params))
|
| +
|
|
|