具有签名URL的Google Storage REST PUT

时间:2013-05-17 14:03:56

标签: php javascript upload base64 google-cloud-storage

我正在尝试使用签名网址作为身份验证,直接通过javascript将图片的base64数据上传到Google存储空间,这显然可以做到。

根据developers.google.com/storage/docs/reference-methods#putobject,只需要设置六个标头即可实现此功能。同样对于标题'授权',我试图在这里使用最后一个选项:

developers.google.com/storage/docs/reference-headers#authorization

哪个是'签名'developers.google.com/storage/docs/authentication#service_accounts

我唯一想要使用PHP的是获取签名。以下是我一直努力工作的结果。

PHP& JS页面/代码

<?php

$theDate   = Date(DATE_RFC822);

function signedURL( $filename, $bucket, $method = 'PUT' ) {
    $signature  = "";
    $duration   = 30;
    $emailID    = "980000000000-ytyertyr@developer.gserviceaccount.com";
    $certs      = array();
    $priv_key   = file_get_contents("9999999999999999999999999999-privatekey.p12");

  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\n\n" . $expires . "\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://storage.googleapis.com/' . $bucket . '/' . $filename . '?GoogleAccessId=' . $emailID . '&Expires=' . $expires . '&Signature=' . $signature
         );
    openssl_free_key($RSAPrivateKey);
} 
?>
<script>
var base64img  = 'data:image/png;base64,AAABAAIAICA....snip...A';
var xhr        = new XMLHttpRequest();
//PUT test - PUT status "(Canceled)" - OPTION status 200 (OK)
xhr.open("PUT", "<?php echo signedURL('test.png', 'mybucket'); ?>");
//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 (Refused to set unsafe header "Content-Length" )
xhr.send( base64img );
//GET test.txt temp file - working and returning 200 status (signing must be working ?)
/*
xhr.open("GET", "<?php echo signedURL('test.txt', 'mybucket', 'GET'); ?>");
xhr.send();
*/
//
</script>

Cors xml(似乎很好) - 我只在测试时设置了通配符,缓存/最大时间很短

<?xml version="1.0" ?>
<CorsConfig>
    <Cors>
        <Origins>
            <Origin>*</Origin>
        </Origins>
        <Methods>
            <Method>GET</Method>
            <Method>HEAD</Method>
            <Method>OPTIONS</Method>
            <Method>PUT</Method>
        </Methods>
        <ResponseHeaders>
            <ResponseHeader>accept-encoding</ResponseHeader>
            <ResponseHeader>cache-control</ResponseHeader>
            <ResponseHeader>content-length</ResponseHeader>
            <ResponseHeader>content-type</ResponseHeader>
            <ResponseHeader>expect</ResponseHeader>
            <ResponseHeader>if-modified-since</ResponseHeader>
            <ResponseHeader>origin</ResponseHeader>
            <ResponseHeader>range</ResponseHeader>
            <ResponseHeader>referer</ResponseHeader>
            <ResponseHeader>x-goog-acl</ResponseHeader>
            <ResponseHeader>x-goog-api-version</ResponseHeader>
        </ResponseHeaders>
        <MaxAgeSec>900</MaxAgeSec>
    </Cors>
</CorsConfig>

我已经在文件上测试了GET方法,现在又恢复了200状态(\ n \ n - 修复)

更新

在Firefox中,它确实会返回403,与Chrome不同。

2 个答案:

答案 0 :(得分:3)

所以以下几行很奇怪,因为使用OA将OAuth和PUT混合签名的URL:

# This looks like a PUT to signed URL
xhr.open("PUT", '<?php echo signedURL('imgfile.png','PUT',30,'mybucketname'); ?>', true);
# But multipart requires POST
xhr.setRequestHeader("Content-type", "multipart/form-data; boundary="+boundary);
# And here's a second form of authorization
xhr.setRequestHeader("Authorization", "OAuth <?php echo $signature; ?>");

multipart/form-data上传需要POST动词,适用于html表单:Google Cloud Storage : PUT Object vs POST Object to upload file.?

只要您在XMLHttpRequest中发送自定义标头,我建议您使用 {em> <{em> OAuth credentials {/ 1>}使用PUT

xhr.open("PUT", "https://storage.googleapis.com/mybucketname/imgfile.png");
xhr.setRequestHeader("Authorization", "OAuth Bearer 1234567abcdefg");
xhr.setRequestHeader("Content-Length", raw_img_bytes.length);
xhr.send(raw_img_bytes);

a signed url

xhr.open("PUT", "https://storage.googleapis.com/mybucketname/imgfile.png?" + 
                "GoogleAccessId=1234567890123@developer.gserviceaccount.com&" +
                "Expires=136891473&" +
                "Signature=BClz9e...WvPcwN%2BmWBPqwg...sQI8IQi1493mw%3D");
xhr.setRequestHeader("Content-Length", raw_img_bytes.length);
xhr.send(raw_img_bytres);

答案 1 :(得分:0)

我gess你的Content-Type是已知的(比如Content-Type:video / mp4)?尝试上传未知扩展名的文件。对我来说,PUT正在这种情况下工作,而不是当Content-Type不为空时...... 我不明白为什么......