1

I'm trying to take a base64-encoded PNG, decode it, then upload it to Google Cloud Storage and get its public URL.

I can do it locally by saving the PNG to file and then uploading it with blob.upload_from_file('picture.png'), but App Engine doesn't allow write access to its file structure.

This code works locally but not in Google App Engine, where it fails at with open(...

import base64
from google.cloud import storage

def handler(request):

    pic = request.POST.get('photograph') #Object like 'data:image/png;base64,iVBORw0KGgoAAAAN…'
    data = pic[22:]                
    png = base64.b64decode(data)     
    filename = "pic.png" 
    with open(filename, "wb") as f:
        f.write(png)
    client = storage.Client.from_service_account_json(os.path.abspath('credentials.json'))
    bucket = client.get_bucket('mybucket')
    blob = bucket.blob(filename)        #create destination uri in bucket
    blob.upload_from_file(filename)       

    image_url = "https://storage.googleapis.com/mybucket/" + filename

The GCS docs say to Use the Blob.upload_from_file(), Blob.upload_from_filename(), or Blob.upload_from_string() method to upload an object.

However none of those methods work for this situation because there is no file, just an encoded PNG object. How can I upload it to gcloud storage?

I'm surely missing something obvious. Any ideas would be much appreciated.

ian-campbell
  • 1,605
  • 1
  • 20
  • 42
  • 2
    If you are using AppEngine you shouldn’t need to use specific authentication details. You can replicate this locally by setting the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. Make sure you have given AppEngine the correct permissions to use other Google services because these are denied by default. You can do this in the console. And check out `NamedTemporaryFile` to correctly support multiple concurrent requests. – David May 12 '20 at 21:55
  • A temp folder is exactly what I need. – ian-campbell May 12 '20 at 22:03
  • 1
    Does this answer your question? [How to upload a bytes image on Google Cloud Storage from a Python script](https://stackoverflow.com/questions/46078088/how-to-upload-a-bytes-image-on-google-cloud-storage-from-a-python-script) – Jan Hernandez May 12 '20 at 23:02
  • David's suggestion was perfect. Temporary storage was all I needed. – ian-campbell May 13 '20 at 00:33
  • 1
    @David can you turn your comment into an answer? to help others – Jan Hernandez May 13 '20 at 13:07

2 Answers2

3

As per David's suggestion, this is what worked. I simply added /tmp/ before the filename and App Engine allowed me to save it.

import base64
from google.cloud import storage

def handler(request):
    pic = request.POST.get('photograph') 
    data = pic[22:]                
    png = base64.b64decode(data)     
    filename = "pic.png"

    temp_location = '/tmp/' + filename          #here
    with open(temp_location, "wb") as f:        
        f.write(png)

    client = storage.Client.from_service_account_json(os.path.abspath('credentials.json'))
    bucket = client.get_bucket('mybucket')
    blob = bucket.blob(filename)        
    blob.upload_from_file(temp_location)       

    image_url = "https://storage.googleapis.com/mybucket/" + filename
faremal
  • 50
  • 2
  • 6
ian-campbell
  • 1,605
  • 1
  • 20
  • 42
1
import base64
from google.cloud import storage 
#The post_image_base64 is base64 string
base64_img_bytes = post_image_base64.encode('utf-8')

#this for localhost if you want to upload it to gcloud storage then you have to add this path /tmp/ 

"""
with open('/tmp/'+'decoded_image.png', 'wb') as file_to_save:
   decoded_image_data = base64.decodebytes(base64_img_bytes)
"""
with open('decoded_image.png', 'wb') as file_to_save:
   decoded_image_data = base64.decodebytes(base64_img_bytes)
   ##Save file 
   #file_to_save.write(decoded_image_data)


gcs=storage.Client.from_service_account_json(os.path.abspath('credentials.json'))  
bucket = gcs.get_bucket("bucket_name")
blob = bucket.blob("image_name.png")

#for upload to gcloud storage you also need to add /tmp/ path in here
#blob.upload_from_string("/tmp/"+decoded_image_data)

blob.upload_from_string(decoded_image_data)

#and then make it public use this
blob = bucket.get_blob('image_name.png')
blob_url = blob.make_public()            
print(blob.public_url);
Sammrafi
  • 399
  • 4
  • 8
  • Last time I forget to add credentials.json.........because of I'm calling that from another part so.and Thank you for edit suggestion @ian-campbell – Sammrafi Jul 07 '21 at 07:11