search
LoginSignup
1

More than 1 year has passed since last update.

posted at

GmailのAPIでメール取得が出来ているけどsizeが0になってしまう場合の対処

GmailのAPIを触ってみてますが、メールによって取得できるパターン取得できないパターンがありました。

まずはどんなコードか

"Google公式ライブラリを利用してNode.jsからGmailの送受信をしてみよう"などを参考にコードを書いてメール取得をしてみています。

users.messages.list()でメールを検索し、users.messages.get()でメール本文を取得します。

//省略

  const gmail = google.gmail({version: 'v1', auth});


  //1件検索
  const resList = await gmail.users.messages.list({userId: 'me', q: 'from:hoge@hoge.com'});
  const lastMessage = resList.data.messages[0];


  //検索したメールIDで中身を取得
  const res = await gmail.users.messages.get({
    userId: 'me',
    id: lastMessage.id,
    format: 'FULL'
  });

  const buf = new Buffer.from(res.data.payload.body.data, 'base64');
  const str = buf.toString();
  console.log(str,str.length);

//省略

通常だと、res.data.payload.body.dataにメール本文が入ってきて、メール本文がコンソールに表示されます。

メールが空の場合がありました。

Gmailなどのメーラー的にはちゃんと表示されてるのですが、このコードのconsole.logで表示がされないケースがありました。

こんな感じのエラーです。

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined

new Buffer.from(res.data.payload.body.data, 'base64');と指定している第1引数に文字列を入れろと怒られてます。

console.log(resMes.data.payload);で調べてみると{ size: 0 }となっていました。

body: { size: 0 },
  parts: [
    {
      partId: '0',
      mimeType: 'text/plain',
      filename: '',
      headers: [Array],
      body: [Object]
    },
    {
      partId: '1',
      mimeType: 'text/html',
      filename: '',
      headers: [Array],
      body: [Object]
    }
  ]

さらにbodyではなく、partsの方にBodyが入ってそうです。
console.log(resMes.data.payload.parts[0]);などでみてみるとやはり入ってます。

{
  partId: '0',
  mimeType: 'text/plain',
  filename: '',
  headers: [
    { name: 'Content-Type', value: 'text/plain; charset=utf-8' },
    { name: 'Content-Transfer-Encoding', value: 'base64' }
  ],
  body: {
    size: 11661,
    data: '44GT44KT44Gr44Gh44Gv44CB44G144KL44GV44Go44OB44On44Kk44K544...

payload.bodyではなくpayload.partsに本文がいるケースがある

ということで、bodyではなくparts側に本文のbase64文字列が格納されている場合があります。

何個か試してみましたが、HTMLメールなどはそうなってるのかもしれません。

GoogleのAPI側でその辺吸収してくれても良いのになぁ...

ということで、bodyが空の場合はpartsからbase64文字列をchunkとして取得して、partsの数だけ結合するようにしました。


    let mailData = '';

    //bodyが場合
    if(resMes.data.payload.body.size !== 0){
        mailData = resMes.data.payload.body.data;
    }

    //bodyが空の場合はpartsから取得
    else{
        const parts = resMes.data.payload.parts;
        for (let i = 0, len=parts.length; i < len; i++) {
            if(parts[i].body.size === 0) continue;
            const chunk = parts[i].body.data;
            mailData += chunk;
        }
    }
    const buf = new Buffer.from(mailData, 'base64');
    const str = buf.toString();

これで今のところbodyが空の場合のメール本文も取得することができています。

まとめ

メールの仕様なんですかね?

APIの返却値がpayload.partsの奥にある場合と、payload.dataに存在する場合があるという話でした。

Gmail API触ってる記事はまだまだ少ない気がするので参考になれば幸いです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1