Tholle Tholle - 4 months ago 22
Ajax Question

Mail attachment wrong media type Gmail API

I'm trying to send a message with a jpeg file attached through the Gmail API in Javascript client side. The code I've written so far is as follows:

$.ajax({
type: "POST",
url: "https://www.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=multipart",
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'multipart/related; boundary="foo_bar_baz"'
},
data: data
});


Where
data
is a string built up like the example found here:

--foo_bar_baz
Content-Type: application/json; charset=UTF-8

{
"raw": "RnJvbTogRW1pbCBUaG9saW4gPGVtdGhvbGluQGdtYWlsLmNvbT4KVG86IEV4YW1wbGUgTmFtZSA8ZW10aG9saW5AZ21haWwuY29tPgpTdWJqZWN0OiBzZHNkCgpzZHNk"
}

--foo_bar_baz
Content-Type: image/jpeg

data:image_jpeg;base64,_9j_4AAQSkZJRgABAQEAYABgAAD_2wBDAAIBAQIBAQICAgICAgIC…bHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3-Pn6_9oADAMBAAIRAxEAPwD-f-iiigD_2Q==

--foo_bar_baz--


The error I get is
Media type 'image/jpeg' is not supported. Valid media types: [message/rfc822]
, which is understandable since
[message/rfc822]
is the only valid MIME-type for the media according to the specification, but the example linked above states otherwise.

What am I doing wrong? It would be much appreciated if someone could shed some light on this!

Answer

EDIT

This first piece of code works for attachments with a combined size of a few mb. If you want to use the allowed limit of 35 mb, check the edit at the end of the answer.


After Steve pushed me in the right direction (the entire mail has to be in the "raw"-parameter), I simply tried the Python API and looked at the mail generated by that.

Mail withouth attachment

Content-Type: text/plain; charset="UTF-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
to: receiver@gmail.com
from: sender@gmail.com
subject: Subject Text

The actual message text goes here

Mail with attachment

Content-Type: multipart/mixed; boundary="foo_bar_baz"
MIME-Version: 1.0
to: receiver@gmail.com
from: sender@gmail.com
subject: Subject Text

--foo_bar_baz
Content-Type: text/plain; charset="UTF-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

The actual message text goes here

--foo_bar_baz
Content-Type: image/jpeg
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="example.jpg"

{JPEG data}

--foo_bar_baz--

So I just wrote my code around this, and it worked great!

var mail = "";
mail += "From: sender@gmail.com\n";
mail += "To: receiver@gmail.com\n";
mail += "Subject: Subject Text";
// If there are no attachments, the content is simply plain text
if(attachment == null)
{
  mail = 'Content-Type: text/plain; charset="UTF-8"\n' + mail;
  mail += "\n\n" + message;
  sendMessage();
}
// If there is an attachment, the type is mulipart/mixed
else {
  mail = 'Content-Type: multipart/mixed; boundary="foo_bar_baz"\n' + mail + '\n\n';

  // The regular message
  mail += '--foo_bar_baz\n';
  mail += 'Content-Type: text/plain; charset="UTF-8"\n\n';
  mail += message + '\n';

  var reader = new FileReader();
  reader.onloadend = function (e) {
    // The relevant base64-encoding comes after "base64,"
    var result = e.target.result.split('base64,')[1];
    mail += '--foo_bar_baz\n';
    mail += 'Content-Type: ' + attachment.type + '\n';
    mail += 'Content-Transfer-Encoding: base64\n';
    mail += 'Content-Disposition: attachment; filename="' + attachment.name + '"\n\n';

    mail += result + '\n';
    mail +=  '--foo_bar_baz--';
    sendMessage();
  }

  reader.readAsDataURL(attachment);

  function sendMessage() {
    // The Gmail API doesn't like regular btoa-encoding. Replace '+' with '-', and '/' with '_'
    mail = btoa(mail).replace(/\+/g, '-').replace(/\//g, '_');

    $.ajax({
      type: "POST",
      url: "https://www.googleapis.com/gmail/v1/users/me/messages/send",
      contentType: "application/json",
      dataType: "json",
      beforeSend: function(xhr, settings) {
        xhr.setRequestHeader('Authorization','Bearer ' + accessToken);
      },
      data: JSON.stringify({"raw": mail}),
      success: function(data) {
        log(data);
      },
      error: function(data) {
        log(data);
      }
    });
  }
}

Edit

The code above works, but a few alterations are needed to use the max limit of 35 mb.

With a mail built up as the example under the heading Mail with attachment, the altered ajax-request looks as follows:

$.ajax({
      type: "POST",
      url: "https://www.googleapis.com/gmail/v1/users/me/messages/send?uploadType=multipart",
      contentType: "message/rfc822",
      beforeSend: function(xhr, settings) {
        xhr.setRequestHeader('Authorization','Bearer ' + accessToken);
      },
      data: mail // NOT encoded! The mail should now be in the unchanged rfc822-format.
}); 

The contentType is now "message/rfc822" instead of "application/json", the uri has gotten a new parameter "uploadType=multipart", and most importantly the mail is no longer Base64 encoded, but supplied in the rfc822-format.