Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uploading an image to Picasa from a Chrome Extension

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!

like image 434
Matt Sweeney Avatar asked Jul 19 '11 18:07

Matt Sweeney


People also ask

How do I save pictures from Google Chrome?

1) Install the extension Save Images to Google™ Photos 2) Right Click the image you wish to save and choose the "Save Images to Google™ Photos" option Our software is 100% free and a great way to save your photos to Google™ Photos without having to download the photo and uploading it again from your computer to Google™ ...


1 Answers

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.

like image 126
oliverguenther Avatar answered Oct 12 '22 01:10

oliverguenther