phpgoogle-cloud-platformgoogle-cloud-storagephp-7google-cloud-php-client

Google Storage signedUploadUrl, fetching upload url returns: Bad content type. Please use multipart


I want to let my user upload large files directly to Google Storage. According to this documentation I should be able to use the PHP SDK to generate a signed upload url. I do so like this:

$storage = new StorageClient(['keyFilePath' =>  $myKeyPath ]);

$bucket = $storage->bucket($bucketname);
$object = $bucket->object($relativepath);

$sessionURI = $object->signedUploadUrl(
    # This URL is valid for 15 minutes
    new \DateTime('15 min'),
    [
        'method' => 'PUT',
        'contentType' => 'application/octet-stream',
        'version' => 'v4',
    ]
);

The documentation says I need to POST to the returned $sessionURI in order to then get the ACTUAL url I can upload to. The docs:

signedUploadUrl

Create a Signed Upload URL for this object.

...

In order to upload data, a session URI must be obtained by sending an HTTP POST request to the URL returned from this method.

However, when I try to POST to the $sessionURI, I get the error:

POST object expects Content-Type multipart/form-data

So, I explicitly set the Content-Type to multipart/form-data and then I get the error:

Bad content type. Please use multipart.

I've tried doing this from code as well as using curl on the command line, and I've tried setting the Content-Type but nothing seems to work (I added some new lines to make this readable, but it's all one line originally):

/usr/bin/curl -X POST -H "Content-Type: multipart/form-data" 
https://storage.googleapis.com/my-project-id.appspot.com/uploads/uploadfilename.jpg?GoogleAccessId=myserviceaccount
@mydomain.com&Expires=1591657512&Signature=veryverylongsignatureasdfasdfasdfasdfadfa
sdfasdfasdfasdfasdfasdfasdfasdfasdfadsfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa
sdfasdfasdfasdfasdfasdfasdfasdfasdf

I've tried a bunch of different examples and I've tried just following exactly what the documentation says and nothing seems to work.

What am I missing? What am I doing wrong?

---- UPDATE

I tried to go back to the beginning with Google's sample code, which still didn't work. This was the curl command that Google's sample code was giving me:

curl -X PUT -H 'Content-Type: application/octet-stream' --upload-file /path/to/file.jpg 
https://storage.googleapis.com/my-project-id.appspot.com/uploads/uploadfilename.jpg?GoogleAccessId=myserviceaccount
@mydomain.com&Expires=1591657512&Signature=veryverylongsignatureasdfasdfasdfasdfadfa
sdfasdfasdfasdfasdfasdfasdfasdfasdfadsfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa
sdfasdfasdfasdfasdfasdfasdfasdfasdf

That was giving me:

Anonymous caller does not have storage.objects.create access to the Google Cloud Storage object.


Solution

  • I originally tried the sample code that google provided and it didn't work (which was the two-step signedUrl() method, which just requires one api call and one PUT call), so I went through many iterations trying different functions and different methods, following different examples on the web, all of which kept failing (including the three-step signedUploadUrl() method which requires an api call, a POST, and then another POST (or maybe PUT)).

    I still need to figure out how to get this working within my app, but I at least got it working with curl which is a proof of concept on the permissions and api calls.

    The reason my original curl tests were failing was because the upload url contained standard query params which include & and those were screwing up the command when I ran the curl command via system().

    So, back to the original curl PUT request with the simple signedUrl() approach, the bad command looked like this:

    curl -X PUT -H 'Content-Type: application/octet-stream' --upload-file /path/to/file.jpg 
    https://storage.googleapis.com/my-project-id.appspot.com/uploads/uploadfilename.jpg?GoogleAccessId=myserviceaccount
    @mydomain.com&Expires=1591657512&Signature=veryverylongsignatureasdfasdfasdfasdfadfa
    sdfasdfasdfasdfasdfasdfasdfasdfasdfadsfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa
    sdfasdfasdfasdfasdfasdfasdfasdfasdf
    

    while the working command included quotes around the URL to avoid the shell grabbing and interpreting the &s:

    curl -X PUT -H 'Content-Type: application/octet-stream' --upload-file /path/to/file.jpg 
    "https://storage.googleapis.com/my-project-id.appspot.com/uploads/uploadfilename.jpg?GoogleAccessId=myserviceaccount
    @mydomain.com&Expires=1591657512&Signature=veryverylongsignatureasdfasdfasdfasdfadfa
    sdfasdfasdfasdfasdfasdfasdfasdfasdfadsfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa
    sdfasdfasdfasdfasdfasdfasdfasdfasdf"