Skip to content

Commit

Permalink
Allow custom endpoints with Google Cloud Storage.
Browse files Browse the repository at this point in the history
Add new `GS_CUSTOM_ENDPOINT` setting to allow usage of custom
domains/URLs with Google Cloud Storage, similar to
`AWS_S3_CUSTOM_DOMAIN`.
  • Loading branch information
martey committed Jan 11, 2019
1 parent efcad7f commit 5b42349
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 2 deletions.
5 changes: 5 additions & 0 deletions docs/backends/gcloud.rst
Expand Up @@ -142,6 +142,11 @@ rolled over into a temporary file on disk. Default is 0: Do not roll over.

Sets Cache-Control HTTP header for the file, more about HTTP caching can be found `here <https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#cache-control>`_

``GS_CUSTOM_ENDPOINT`` (optional: default is ``None``)

Sets a `custom endpoint <https://cloud.google.com/storage/docs/request-endpoints>`_,
that will be used instead of ``https://storage.googleapis.com`` when generating URLs for files.

``GS_LOCATION`` (optional: default is ``''``)

Subdirectory in which the files will be stored.
Expand Down
20 changes: 18 additions & 2 deletions storages/backends/gcloud.py
Expand Up @@ -8,13 +8,15 @@
from django.utils import timezone
from django.utils.deconstruct import deconstructible
from django.utils.encoding import force_bytes, smart_str
from django.utils.six.moves.urllib.parse import quote

from storages.utils import (
check_location, clean_name, get_available_overwrite_name, safe_join,
setting,
)

try:
from google.cloud.storage._signing import generate_signed_url
from google.cloud.storage.client import Client
from google.cloud.storage.blob import Blob
from google.cloud.exceptions import NotFound
Expand Down Expand Up @@ -86,6 +88,7 @@ class GoogleCloudStorage(Storage):
project_id = setting('GS_PROJECT_ID')
credentials = setting('GS_CREDENTIALS')
bucket_name = setting('GS_BUCKET_NAME')
custom_endpoint = setting('GS_CUSTOM_ENDPOINT', None)
location = setting('GS_LOCATION', '')
auto_create_bucket = setting('GS_AUTO_CREATE_BUCKET', False)
auto_create_acl = setting('GS_AUTO_CREATE_ACL', 'projectPrivate')
Expand Down Expand Up @@ -262,9 +265,22 @@ def url(self, name):
name = self._normalize_name(clean_name(name))
blob = self.bucket.blob(self._encode_name(name))

if self.default_acl == 'publicRead':
if not self.custom_endpoint and self.default_acl == 'publicRead':
return blob.public_url
return blob.generate_signed_url(self.expiration)
elif self.default_acl == 'publicRead':
return '{storage_base_url}/{quoted_name}'.format(
storage_base_url=self.custom_endpoint,
quoted_name=quote(name.encode('utf-8')),
)
elif not self.custom_endpoint:
return blob.generate_signed_url(self.expiration)
else:
return generate_signed_url(
self.credentials,
resource=quote(name.encode('utf-8')),
api_access_endpoint=self.custom_endpoint,
expiration=self.expiration,
)

def get_available_name(self, name, max_length=None):
name = clean_name(name)
Expand Down
5 changes: 5 additions & 0 deletions tests/test_gcloud.py
Expand Up @@ -358,6 +358,11 @@ def test_url_not_public_file_with_custom_expires(self):
self.assertEqual(url, 'http://signed_url')
blob.generate_signed_url.assert_called_with(timedelta(seconds=3600))

def test_custom_endpoint(self):
self.storage.custom_endpoint = "https://example.com"
url = "{}/{}".format(self.storage.custom_endpoint, self.filename)
self.assertEqual(self.storage.url(self.filename), url)

def test_get_available_name(self):
self.storage.file_overwrite = True
self.assertEqual(self.storage.get_available_name(
Expand Down

0 comments on commit 5b42349

Please sign in to comment.