生成预签名的url,以便使用python将文件上传到Google存储空间

时间:2019-08-06 04:21:57

标签: python google-cloud-platform google-cloud-storage

我想使用javascript ajax功能将图像从前端上传到Google存储。我需要服务器生成的一个预签名的url,它将为前端提供身份验证以上传blob。 使用本地计算机时,如何生成预签名的URL。

以前对于aws s3,我会这样做:

pp = s3.generate_presigned_post(
            Bucket=settings.S3_BUCKET_NAME,
            Key='folder1/' + file_name,  
            ExpiresIn=20  # seconds
        )

为用户生成签名的URL以便仅查看存储在Google存储空间中的文件时,我会这样做:

    bucket = settings.CLIENT.bucket(settings.BUCKET_NAME)
    blob_name = 'folder/img1.jpg'
    blob = bucket.blob(blob_name)
    url = blob.generate_signed_url(
        version='v4',
        expiration=datetime.timedelta(minutes=1),
        method='GET')

3 个答案:

答案 0 :(得分:2)

花了100美元的Google支持费用和我两个星期的时间终于找到了解决方案。

client = storage.Client() # works on app engine standard without any credentials requirements

但是,如果您想使用generate_signed_url()功能,则需要服务帐户Json密钥。

每个应用程序引擎标准都有一个默认服务帐户。 (您可以在IAM /服务帐户中找到它)。为该默认sv帐户创建一个密钥,然后以json格式下载密钥('sv_key.json')。将该密钥存储在app.yaml文件旁边的Django项目中。然后执行以下操作:

from google.cloud import storage
CLIENT = storage.Client.from_service_account_json('sv_key.json')
bucket = CLIENT.bucket('bucket_name_1')
blob = bucket.blob('img1.jpg') # name of file to be saved/uploaded to storage
pp = blob.generate_signed_url(
    version='v4',
    expiration=datetime.timedelta(minutes=1),
    method='POST') 

这将在您的本地计算机和GAE标准上运行。当您将应用程序部署到GAE时,sv_key.json也将与Django项目一起部署,因此它可以工作。

希望它对您有帮助。

答案 1 :(得分:1)

编辑我的答案,因为我不了解您面临的问题。

就像@Nick Shebanov所说的那样,看问题中的注释线程,有一种可能性可以实现将GAE与flex环境一起使用时要达到的目标。

到目前为止,我一直在尝试使用GAE Standard环境做同样的事情,但是没有运气。此时,我建议在public issue tracker处打开功能请求,以某种方式实现。

答案 2 :(得分:0)

  • 创建服务帐户私钥并将其存储在 SecretManager (SM) 中。
  • 在 settings.py 中,从 SecretManager 中检索该密钥并将其存储在常量中 - SV_ACCOUNT_KEY
  • 覆盖 Client() class func from_service_account_json() 以获取 json 密钥内容而不是 json 文件的路径。这样我们就不必在我们的文件系统(本地、cloudbuild 或 GAE 中)中有一个 json 文件。我们可以随时随地从 SM 获取私钥内容。

settings.py

secret = SecretManager()
SV_ACCOUNT_KEY = secret.access_secret_data('SV_ACCOUNT_KEY')

signed_url_mixin.py

import datetime
import json

from django.conf import settings
from google.cloud.storage.client import Client
from google.oauth2 import service_account


class CustomClient(Client):
    @classmethod
    def from_service_account_json(cls, sv_account_key, *args, **kwargs):
        """
        Copying everything from base func (from_service_account_json). 
        Instead of passing json file for private key, we pass 
            sv_account_key (json content) to sign a url. 
        Since its not properly written, we cannot just overwrite a class or a 
        func, we have to write the entire func.
        """
        if "credentials" in kwargs:
            raise TypeError("credentials must not be in keyword arguments")
        credentials_info = json.loads(sv_account_key)
        credentials = service_account.Credentials.from_service_account_info(
            credentials_info
        )
        if cls._SET_PROJECT:
            if "project" not in kwargs:
                kwargs["project"] = credentials_info.get("project_id")

        kwargs["credentials"] = credentials
        return cls(*args, **kwargs)


class _SignedUrlMixin:
    bucket_name = settings.BUCKET_NAME
    CLIENT = CustomClient.from_service_account_json(settings.SV_ACCOUNT_KEY)
    exp_min = 4  # expire minutes

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bucket = self.CLIENT.bucket(self.bucket_name)

    def _signed_url(self, file_name, method):
        blob = self.bucket.blob(file_name)
        signed_url = blob.generate_signed_url(
            version='v4',
            expiration=datetime.timedelta(minutes=self.exp_min),
            method=method
        )
        return signed_url


class GetSignedUrlMixin(_SignedUrlMixin):
    """
    A GET url to view file on CS
    """

    def get_signed_url(self, file_name):
        """
        :param file_name: name of file to be retrieved from CS.
            xyz/f1.pdf
        :return: GET signed url
        """
        method = 'GET'
        return self._signed_url(file_name, method)


class PutSignedUrlMixin(_SignedUrlMixin):
    """
    A PUT url to make a put req to upload a file to CS
    """

    def put_signed_url(self, file_name):
        """
        :file_name: xyz/f1.pdf
        """
        method = 'PUT'
        return self._signed_url(file_name, method)