Picasa data API fun: Creating albums and uploading images

Today, we’re going to have some fun with the Picasa Data API by learning how to create Picasa albums and upload photos to them.

We’re going to do everything with vanilla-PHP and cURL but it’s worth mentioning that the Zend Framework has a set of classes for doing all of this stuff and more.

While it does look like Google is moving in the direction of using oAuth for authentication, they still support username/password client logins which are, frankly, a ton easier to deal with.

I’ve written about Google client logins before and the code I mentioned previously will still work with the Picasa Data API. Basically, we provide a username and password to Google. They respond with an Auth token which we then pass as an HTTP header to all future requests to the API.

Like pretty much all Google Data APIs, doing things with Picasa mostly involves sending XML packets via HTTP POST. It’s just a matter of crafting the appropriate XML for what you’re trying to do.

Creating a Picasa Album

Here’s a simple example which creates an album called “Test album from PHP". The below code assume we’re already authenticated and have a valid Auth token. You’ll also notice that we need to have the userId of the Picasa user we’re authenticated as.

~~~~ {.php name="code"} $authHeader = 'Authorization: GoogleLogin auth="' . $authToken . '"'; $feedUrl = "https://picasaweb.google.com/data/feed/api/user/$userId"; $rawXml = " Test album from PHP

This is a test album Louisville public 1152255600000 "; curl_setopt($ch, CURLOPT_URL, $feedUrl); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); $data = array($xml); $options = array( CURLOPT_SSL_VERIFYPEER=> false, CURLOPT_POST=> true, CURLOPT_RETURNTRANSFER=> true, CURLOPT_HEADER=> true, CURLOPT_FOLLOWLOCATION=> true, CURLOPT_POSTFIELDS=> $rawXml, CURLOPT_HTTPHEADER=> array('GData-Version: 2', $authHeader, 'Content-Type: application/atom+xml') ); curl_setopt_array($ch, $options); $ret = curl_exec($ch); curl_close($ch); wzxhzdk:0

Uploading an image including metadata

This is a little tricker.

The documentation shows an example request as looking like this

~~~~ {.php name="code"} Content-Type: multipart/related; boundary="END_OF_PART"Content-Length: 423478347MIME-version: 1.0Media multipart posting--END_OF_PARTContent-Type: application/atom+xml plz-to-love-realcat.jpg Real cat wants attention too. term="http://schemas.google.com/photos/2007#photo"/>--END_OF_PARTContent-Type: image/jpeg...binary image data...--END_OF_PART-- wzxhzdk:1

That’s your HTTP header. The string END_OF_PART is just how you tell the server when one content section has ended and the next section starts. It just needs to be a unique string.

The Content-Length is also tricky. I just assumed that it was calculated by adding the length of the metadata XML and the image file size. I spent a fair amount of time banging my on that until I found this snippet from the YouTube Data API.

Here’s the key takeaway:

>

To calculate the proper Content-Length, you need to count the full string length of the POST request. However, in addition to the XML component and the file binary, a direct upload request also defines a boundary string that separates the different parts of the request. So the calculation of the Content-Length needs to account for the size of the XML and file binary as well as of the inserted boundary strings and newlines.

Here’s a code example of uploading an image including metadata.

~~~~ {.php name="code"} $albumUrl = "https://picasaweb.google.com/data/feed/api/user/$userId/albumid/$albumId"; $imgName = $_SERVER['DOCUMENT_ROOT'] . '/picasa/cute_baby_kitten.jpg'; $rawImgXml = ' plz-to-love-realcat.jpg Real cat wants attention too. term="http://schemas.google.com/photos/2007#photo"/> '; $fileSize = filesize($imgName); $fh = fopen($imgName, 'rb'); $imgData = fread($fh, $fileSize); fclose($fh); $dataLength = strlen($rawImgXml) + $fileSize; $data = ""; $data .= "\nMedia multipart posting\n"; $data .= "--P4CpLdIHZpYqNn7\n"; $data .= "Content-Type: application/atom+xml\n\n"; $data .= $rawImgXml . "\n"; $data .= "--P4CpLdIHZpYqNn7\n"; $data .= "Content-Type: image/jpeg\n\n"; $data .= $imgData . "\n"; $data .= "--P4CpLdIHZpYqNn7--"; $header = array('GData-Version: 2', $authHeader, 'Content-Type: multipart/related; boundary=P4CpLdIHZpYqNn7;', 'Content-Length: ' . strlen($data), 'MIME-version: 1.0'); $ret = ""; $ch = curl_init($albumUrl); $options = array( CURLOPT_SSL_VERIFYPEER=> false, CURLOPT_POST=> true, CURLOPT_RETURNTRANSFER=> true, CURLOPT_HEADER=> true, CURLOPT_FOLLOWLOCATION=> true, CURLOPT_POSTFIELDS=> $data, CURLOPT_HTTPHEADER=> $header ); curl_setopt_array($ch, $options); $ret = curl_exec($ch); curl_close($ch); wzxhzdk:2

And the actual request body ends up looking like this:

~~~~ {.php name="code"} Media multipart posting--P4CpLdIHZpYqNn7Content-Type: application/atom+xml plz-to-love-realcat.jpg Real cat wants attention too. term="http://schemas.google.com/photos/2007#photo"/> --P4CpLdIHZpYqNn7Content-Type: image/jpegIMAGE DATA GOES HERE--P4CpLdIHZpYqNn7-- ~~~~

Then, if all goes well, you’ll end up with something like this:

image

There are lots of neat things you can do with the Picasa data API.

Hopefully the above will be enough to get you rolling with them.