Reputation: 21870
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.
Upvotes: 2
Views: 1422
Reputation: 21870
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"
Upvotes: 0
Reputation: 3516
If you are inviting users to upload files via HTML forms, you'll want to use Bucket::generatedSignedPostPolicyV4
.
That method will return an array containing an action URI (for your <form action>
property) and a list of fields which you need to include in the form. Iterate over those and embed them as key/value fields using hidden inputs.
The formatting for the sample on the link above seems to be a bit messed up, but you can see a complete (if simplified) sample in the source code.
Upvotes: 1
Reputation: 421
It seems the endpoint you are POSTing to does not accept form multipart data. Try setting your Content-Type to "multipart/related" instead of "multipart/form-data" and see if you are able to proceed. Check this link Cloud Storage Upload for more help.
Upvotes: 0