9

I'm trying to upload an image to Picasa from a Google Chrome Extension and running into some trouble constructing the POST.

This is the protocol google specifies for uploading an image to Picasa (link):

Content-Type: multipart/related; boundary="END_OF_PART"
Content-Length: 423478347
MIME-version: 1.0

Media multipart posting
--END_OF_PART
Content-Type: application/atom+xml

<entry xmlns='http://www.w3.org/2005/Atom'>
  <title>plz-to-love-realcat.jpg</title>
  <summary>Real cat wants attention too.</summary>
  <category scheme="http://schemas.google.com/g/2005#kind"
    term="http://schemas.google.com/photos/2007#photo"/>
</entry>
--END_OF_PART
Content-Type: image/jpeg

...binary image data...
--END_OF_PART--

And this is what I've cobbled together to attempt to do that, borrowing code from here and the extension "clip-it-good":

function handleMenuClick(albumName, albumId, data, tab) {
  chrome.pageAction.setTitle({
    tabId: tab.id,
    title: 'Uploading (' + data.srcUrl.substr(0, 100) + ')'
  });
  chrome.pageAction.show(tab.id);

  var img = document.createElement('img');
  img.onload = function() {
    var canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    canvas.getContext('2d').drawImage(img, 0, 0);

    var dataUrl = canvas.toDataURL();
    var dataUrlAdjusted = dataUrl.replace('data:image/png;base64,', '');

    var builder = new WebKitBlobBuilder();
    builder.append(Base64.decode(dataUrlAdjusted).buffer);

    function complete(resp, xhr) {
      chrome.pageAction.hide(tab.id);
      if (!(xhr.status >= 200 && xhr.status <= 299)) {
        alert('Error: Response status = ' + xhr.status +
              ', response body = "' + xhr.responseText + '"');
      }
    }  // end complete

    OAUTH.authorize(function() {
      OAUTH.sendSignedRequest(
        'http://picasaweb.google.com/data/feed/api/' +
        'user/default/albumid/' + albumId,
        complete,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'multipart/related; boundary=END_OF_PART',
            'MIME-version': '1.0'
          },
          parameters: {
            alt: 'json'
          },
          body: constructContentBody_('title.jpg', 'photo',
                                      builder.getBlob('image/png'),
                                      'image/jpeg', 'lolz')
        }
      );
    });
  }  // end onload

  img.src = data.srcUrl;
}

function constructAtomXml_(docTitle, docType, docSummary) {
  var atom = ['<entry xmlns="http://www.w3.org/2005/Atom">',
              '<title>', docTitle, '</title>',
              '<summary>', docSummary, '</summary>',
              '<category scheme="http://schemas.google.com/g/2005#kind"', 
              ' term="http://schemas.google.com/docs/2007#', docType, '"/>',
              '</entry>'].join('');
  return atom;
};


function constructContentBody_(title, docType, body, contentType, summary) {
  var body_ = ['--END_OF_PART\r\n',
              'Content-Type: application/atom+xml;\r\n\r\n',
              constructAtomXml_(title, docType, summary), '\r\n',
              '--END_OF_PART\r\n',
              'Content-Type: ', contentType, '\r\n\r\n',
              eval(body), '\r\n',
              '--END_OF_PART--\r\n'].join('');
  return body_;
};

This returns "Error: Response status = 400, response body = "Invalid kind on entry.""

I'm not sure if I'm doing something wrong with WebKitBlobBuilder or if my POST is improperly formed. Any suggestions would be welcome!

Matt Sweeney
  • 2,060
  • 1
  • 14
  • 19
  • I guess the problem is that the server expect: Content-Type: image/jpeg and you are giving it base64... – Ido Green Jul 26 '11 at 00:15
  • I might be missing something, but why do you have `builder.getBlob('image/png')` when the content type elsewhere is jpeg? – Sam Dutton Aug 26 '11 at 14:00

1 Answers1

2

My multipart approach looks pretty much the same, except that I load the file through FileReader.readAsBinaryString(file). However, Picasa returns a Bad Request with "Not an Image" (for JPG/PNG files) or "Not a valid image" for BMP files.

If you instead post the picture directly, it does work (201 Created). The following code works for me:

function upload_image(file, albumid) {
// Assuming oauth has been initialized in a background page
var oauth = chrome.extension.getBackgroundPage().oauth; 
var method = 'POST';
var url = 'https://picasaweb.google.com/data/feed/api/user/default/albumid/' + albumid;
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader("GData-Version", '3.0');
xhr.setRequestHeader("Content-Type", file.type);
xhr.setRequestHeader("Authorization", oauth.getAuthorizationHeader(url, method, ''));
xhr.onreadystatechange = function(data) {
    if (xhr.readyState == 4) {
        on_image_sent(data, xhr);
    }
};
xhr.send(file);

Where file is a HTML5 file object from the FileIO API.

oliverguenther
  • 1,167
  • 1
  • 17
  • 31