0
votes

When uploading to Google cloud storage using a PUT request with base64 data the image (PNG) does not display in the browser and says that it contains errors (when viewing in FF).

After opening the image file in a text editor I can see the base64 data "data:image/png;base64,iVBORw...", so it does not get converted to binary.

Am I meant to upload the binary or is there a way to get this to work with base64 in the HTTP body ?

Basic Code Sample.


    <?php

    $theDate    = Date(DATE_RFC822);
    $emailID    = "*******.gserviceaccount.com";
    $priv_key   = file_get_contents("*******-privatekey.p12");

    function signedURL( $filename, $bucket, $method = 'PUT' ) {
      global $emailID;
      global $priv_key;

       $signature  = "";
       $duration   = 60;
       $certs      = array();
          if (!openssl_pkcs12_read($priv_key, $certs, 'notasecret')) { echo "Unable to parse the p12 file. OpenSSL error: " . openssl_error_string(); exit(); }

        $expires = time() + $duration;
        $to_sign = ( $method . "\n\nimage/png; charset=UTF-8\n" . $expires . "\nx-goog-acl:public-read\n" . "/" . $bucket . "/" . $filename ); 
        $RSAPrivateKey = openssl_pkey_get_private($certs["pkey"]);

      if (!openssl_sign( $to_sign, $signature, $RSAPrivateKey, 'sha256' ))
      {
        error_log( 'openssl_sign failed!' );
        $signature = 'failed';
      } else {
        $signature =  urlencode( base64_encode( $signature ) );
      }

      return ('http://' . $bucket . '/' . $filename . '?GoogleAccessId=' . $emailID . '&Expires=' . $expires . '&Signature=' . $signature);
        openssl_free_key($RSAPrivateKey);
    } 

    $UploadURL = signedURL('test.png', 'mybucket.mydomain.net', 'PUT');

    //echo $UploadURL;

    ?>

    <script>
    var base64img  = "data:image/png;base64,iVB...";//snipped
    var xhr        = new XMLHttpRequest();

    xhr.open("PUT", "<?php echo $UploadURL ?>");
    xhr.setRequestHeader("Content-type", "image/png");
    xhr.setRequestHeader("x-goog-acl", "public-read"); //try to set public read on file
    xhr.setRequestHeader("Content-Length", base64img.length); // Chrome throws error
    xhr.send( base64img );
    </script>

I feel that it is possible based on things that I've read online, but I can't find any examples showing exactly the way it is done with PUT to indicate what is missing to make it work.

1
I'm confused, why do you want to upload base 64 encoded says when you could simply upload the binary data? A binary upload is significantly more efficient. - Benson
Because I thought that I read somewhere that base64 data on average was about 33% smaller, but now you've said that I'm wondering if I read that wrong. If that is the case then I'll go ahead with the blob. Thanks for the reply. - Tom
o.k, I really did read it wrong.. stackoverflow.com/questions/9722603/… .. :) - Tom
Yeah, you definitely don't want to b64 if you're hoping for better efficiency. Is it worth me rewriting that as an answer? - Benson
Hi, thanks again and yeah sure, is that how we close these things ?... I'm new to stack. - Tom

1 Answers

0
votes

You typically want to avoid using base64 encoding unless you really need it. Encoding something in base64 incurs a size overhead, and also requires decoding before it can be used, which is effectively a CPU overhead. Essentially, you only want b64 encode something if the protocol and transport you're using can't handle binary data.

So, in your case I think the answer is to avoid base64 and just upload raw bytes. In the general case, if you do base64 encode something before storing it in Cloud Storage, you'll need to plan for a decode step in whatever software is pulling it back out.