11

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!

Tholle
  • 108,070
  • 19
  • 198
  • 189
  • I am trying to do same thing with objective-c. Can you help me? http://stackoverflow.com/questions/28532808/send-e-mail-with-attachments-using-gmail-rest-api-objective-c – amone Feb 16 '15 at 01:20

2 Answers2

16

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 without 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 reader = new FileReader();
reader.readAsDataURL(attachment);
reader.onloadend = function (e) {
  // The relevant base64-encoding comes after "base64,"
  var jpegData = e.target.result.split('base64,')[1];
  var mail = [
    'Content-Type: multipart/mixed; boundary="foo_bar_baz"\r\n',
    'MIME-Version: 1.0\r\n',
    'to: receiver@gmail.com\r\n',
    'from: sender@gmail.com\r\n',
    'subject: Subject Text\r\n\r\n',

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

    'The actual message text goes here\r\n\r\n',

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

    jpegData, '\r\n\r\n',

    '--foo_bar_baz--'
  ].join('');

  // The Gmail API requires url safe Base64 
  // (replace '+' with '-', replace '/' with '_', remove trailing '=')
  mail = btoa(mail).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

  $.ajax({
    type: "POST",
    url: "https://www.googleapis.com/gmail/v1/users/me/messages/send",
    headers: {
      'Authorization': 'Bearer ' + accessToken,
      'Content-Type': 'application/json'
    },
    data: JSON.stringify({
      raw: mail
    })
  });
}

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",
  headers: {
    'Authorization': 'Bearer ' + accessToken,
    'Content-Type': 'message/rfc822'
  },
  data: mail
}); 

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

Tholle
  • 108,070
  • 19
  • 198
  • 189
  • i am having the same problem, but on the server. Do you have an idea as to which node library should i be looking at? – anurag_29 Jul 28 '14 at 12:46
  • 1
    i modified this code for nodejs. Works great. Thanks for your help. :) – anurag_29 Jul 29 '14 at 08:03
  • 1
    Double new line after boundary is illegal. Tried to edit, but stackoverflow does not allow edits nder 6 chars... – Mikael Feb 25 '15 at 00:27
  • How can I obtain my "accessToken?" – Grant Miller May 15 '17 at 15:09
  • @goatmeal You could use the [OAuth Playground](https://developers.google.com/oauthplayground/) for testing purposes. Just select the Gmail API in the list to left and follow the outlined steps to get the token. – Tholle May 15 '17 at 15:17
  • Okay, I see now that I must exchange the Authorization Code for the Refresh Token and Access Token. Is there a Gmail Javascript API procedure that can be used to return the Authorization Code itself? – Grant Miller May 15 '17 at 15:57
  • How to encode the attached image or file, because I am having porblems in encoding the image. I am using java – AJN Nov 10 '17 at 09:33
2

The docs are a little confusing as the upload docs are generic instructions for how uploads work with Google API and the example isn't quite appropriate for gmail. That said, there is a more detailed explanation of how to send messages and attachments with the gmail API that covers it.

In short, the attachment needs to be encoded in the raw message itself before you send it.

Steve Bazyl
  • 11,002
  • 3
  • 21
  • 24
  • Thank you for your reply Steve! I still don't quite understand, since the neat python API abstracts away some things I need to do manually in a regular Ajax-call in Javascript. Is there a lot I need to add to the answer to this question to get the attachment in the mail? http://stackoverflow.com/questions/24460422/how-to-send-a-message-successfully-using-the-new-gmail-rest-api I used his approach for a mail without attachment, and it worked great! – Tholle Jul 23 '14 at 19:39
  • 1
    Yeah, without a library it's a bit of a pain. For JS, you might be able to use https://github.com/connrs/node-email-stream to construct the raw messages. – Steve Bazyl Jul 24 '14 at 21:04
  • Maybe I should have been more specific in my question. I'm trying to do this client side, so the cool library you just linked will not work sadly. Do you have another card up your sleeve? – Tholle Jul 25 '14 at 08:01
  • 1
    One option would be to just use browserify to package it up for use on the client. Or just borrow code from there as needed. – Steve Bazyl Jul 25 '14 at 20:24