Choosing Google Cloud Storage object name when uploading a file using Python blobstore API

715 views Asked by At

I am migrating from the blobstore to Google Cloud Storage in my Google App Engine python app. I would like to use the blobstore API when uploading to GCS. That is perfectly OK, according to GAE python documentation regarding blobstore

The flow works just like for storing to blobstore. The client requests upload URL, blobstore.create_upload_url(...) creates it, client uploads the file via multipart POST, and then server redirects to the upload handler in my GAE application.

The problem with this is that though I got to choose the GCS bucket (it is one of the parameters of the create_upload_url call) I don't see how I can choose the file name. I would like to break it down into folders. The inspiration comes from the

Google App Engine Blobstore to Google Cloud Storage Migration Tool where the filename is broken so that browsing of the GCS "folders" is manageable.

I know of just one way of naming the GCS file - to forego blobstore API. Instead the client would upload the file to the GAE handler, which would use lib.cloudstorage to write the file data into the GCS - exacly like the quoted Migration tool does.

I would lose GCS goodies like retries on errors during upload.

The question: is there a way to upload file directly to GCS, while impacting the way the resulting GCS object is named.

1

There are 1 answers

0
voscausa On

Repository : https://github.com/voscausa/appengine-gcs-upload

Example code for the first option.

  • where you can name the bucket object
  • and use a signed url to access the bucket

Docs: https://cloud.google.com/storage/docs/reference-methods?hl=bg#postobject

The upload template:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>'gcs_upload'</title>
</head>
<body>
    <form action="http://storage.googleapis.com/{{ form_bucket }}"
          method="post" enctype="multipart/form-data">
        <input type="text" name="key" value="">
        <input type="hidden" name="GoogleAccessId" value="{{ form_access_id }}">
        <input type="hidden" name="acl" value="bucket-owner-read">
        <input type="hidden" name="success_action_redirect" value="{{ form_succes_redirect }}">
        <input type="hidden" name="policy" value="{{ form_policy }}">
        <input type="hidden" name="signature" value="{{ form_signature }}">
        <input type="file" name="file">
        <input type="submit" value="Upload">
    </form>
</body>
</html>

The upload form handler:

class GcsUpload(BaseHandler):

    def get(self):

        default_bucket = app_identity.get_default_gcs_bucket_name()
        google_access_id = app_identity.get_service_account_name()
        succes_redirect = 'http://www.example.com/success'
        policy_string = """
        {"expiration": "2015-06-22T18:11:11Z",
                  "conditions": [
                      ["starts-with", "$key", ""],
                      {"acl": "bucket-owner-read"},
                      {"success_action_redirect": "%s"},
                  ]}""" % succes_redirect

        policy = base64.b64encode(policy_string)
        _, signature_bytes = app_identity.sign_blob(policy)
        signature = base64.b64encode(signature_bytes)

        self.render_template('gcs_upload.html', form_bucket=default_bucket, form_succes_redirect=succes_redirect,
                             form_access_id=google_access_id, form_signature=signature, form_policy=policy)