【发布时间】:2016-05-16 08:26:34
【问题描述】:
我正在使用 App Engine 编写一个新应用程序,并且由于文档建议不要使用 Blobstore API,我正在使用 Google Cloud Storage 客户端 (GCS)。一切都很好,但我希望能够将“签名的 url”返回给客户端,这样他们就可以在不通过应用程序的情况下获取 GCS 资源。我相信这就是签名网址的用途。
但是如何测试呢?我可以成功测试来自客户端的 GCS 调用,但我不知道如何使用 urlfetch 测试客户端的 HTTP 调用。
以下是说明我的问题的完整测试用例:
import base64
import mimetypes
import urllib
import urllib2
from datetime import datetime, timedelta
import time
from google.appengine.api import app_identity
from google.appengine.datastore import datastore_stub_util
from google.appengine.ext import testbed
from google.appengine.ext import ndb
import unittest
import cloudstorage
# IS THIS RIGHT ?
GCS_API_ACCESS_ENDPOINT = 'http://localhost:8000/_ah/gcs'
def sign_url(bucket_object, expires_after_seconds=60):
""" cloudstorage signed url to download cloudstorage object without login
Docs : https://cloud.google.com/storage/docs/access-control?hl=bg#Signed-URLs
API : https://cloud.google.com/storage/docs/reference-methods?hl=bg#getobject
"""
# source: https://github.com/voscausa/appengine-gcs-signed-url/blob/05b8a93e2777679d40af62cc5ffce933216e6a85/sign_url.py
method = 'GET'
gcs_filename = urllib.quote(bucket_object)
content_md5, content_type = None, None
# expiration : number of seconds since epoch
expiration_dt = datetime.utcnow() + timedelta(seconds=expires_after_seconds)
expiration = int(time.mktime(expiration_dt.timetuple()))
# Generate the string to sign.
signature_string = '\n'.join([
method,
content_md5 or '',
content_type or '',
str(expiration),
gcs_filename])
signature_bytes = app_identity.sign_blob(signature_string)[1]
google_access_id = app_identity.get_service_account_name()
# Set the right query parameters. we use a gae service account for the id
query_params = {'GoogleAccessId': google_access_id,
'Expires': str(expiration),
'Signature': base64.b64encode(signature_bytes)}
# Return the built URL.
result = '{endpoint}{resource}?{querystring}'.format(endpoint=GCS_API_ACCESS_ENDPOINT,
resource=gcs_filename,
querystring=urllib.urlencode(query_params))
return result
FILE_DATA = "This is file contents."
MIME = "text/plain"
class TestGCS(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self.policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=0)
self.testbed.init_datastore_v3_stub(consistency_policy=self.policy)
self.testbed.init_app_identity_stub()
self.testbed.init_memcache_stub()
self.testbed.init_urlfetch_stub()
self.testbed.init_blobstore_stub()
ndb.get_context().clear_cache()
def tearDown(self):
self.testbed.deactivate()
def test_gcs_works(self):
with cloudstorage.open('/mybucket/test.txt', 'w', content_type=MIME) as f:
f.write(FILE_DATA)
with cloudstorage.open('/mybucket/test.txt', 'r') as f:
data = f.read()
print(data)
self.assertEqual(data, FILE_DATA)
def test_signurl(self):
url = sign_url('/mybucket/test.txt')
# FIXME: Not yet working as we have no idea on how to access local GCS during the test.
result = urllib2.urlopen(url)
self.assertEqual(200, result.code)
self.assertEqual(FILE_DATA, result.read())
【问题讨论】:
-
GCS_API_ACCESS_ENDPOIND 没有本地端点。您只能使用:storage.googleapis.com。 localhost:8000/_ah/gcs 是本地/SDK Google Google Cloud Storage 客户端库的端点,供您的 test_gcs_works() 使用
-
@voscausa 你的意思是我不能从本地开发测试 GCS HTTP 访问? test_gcs_works 仅使用 cloudstorage api,但我需要测试 HTTP 访问。我打算在应用程序中做的是将用户重定向到我创建的签名 URL,这样我就不必直接从应用程序请求周期提供文件。
-
您可以在SDK中测试GCS和service_accounts,但使用签名url时没有本地appengine GCS服务。
-
新手元问题:如何将此评论标记为帖子的正确答案? :( 或者您可以发布一个答案,说明 GCS URL 无法测试,只能进行 API 调用?谢谢。
标签: google-app-engine google-cloud-storage google-app-engine-python