无法使用Gmail PHP API获取电子邮件正文

无法使用Gmail PHP API获取电子邮件正文,php,gmail-api,Php,Gmail Api,我在使用Gmail PHP API时遇到了问题 我想检索电子邮件的正文内容,但我只能检索带有附件的电子邮件!我的问题是为什么 以下是我目前的代码: // Authentication things above... $client = getClient(); $gmail = new Google_Service_Gmail($client); $list = $gmail->users_messages->listUsersMessages('me', ['maxResu

我在使用Gmail PHP API时遇到了问题

我想检索电子邮件的正文内容,但我只能检索带有附件的电子邮件!我的问题是为什么

以下是我目前的代码:

// Authentication things above...
$client = getClient();
$gmail = new Google_Service_Gmail($client);    
$list = $gmail->users_messages->listUsersMessages('me', ['maxResults' => 1000]);

while ($list->getMessages() != null) {   
    foreach ($list->getMessages() as $mlist) {               
        $message_id = $mlist->id;   
        $optParamsGet2['format'] = 'full';
        $single_message = $gmail->users_messages->get('me', $message_id, $optParamsGet2);

        $threadId = $single_message->getThreadId();
        $payload = $single_message->getPayload();
        $headers = $payload->getHeaders();
        $parts = $payload->getParts();
        //print_r($parts); PRINTS SOMETHING ONLY IF I HAVE ATTACHMENTS...
        $body = $parts[0]['body'];
        $rawData = $body->data;
        $sanitizedData = strtr($rawData,'-_', '+/');
        $decodedMessage = base64_decode($sanitizedData); //should display my body content
    }

    if ($list->getNextPageToken() != null) {
        $pageToken = $list->getNextPageToken();
        $list = $gmail->users_messages->listUsersMessages('me', ['pageToken' => $pageToken, 'maxResults' => 1000]);
    } else {
        break;
    }
}

我知道的第二种检索内容的方法是使用标题部分中的代码段,但它只检索前50个左右的字符,这不是很有用。

让我们做一个小实验。我给自己发了两条信息。一个有附件,一个没有

请求:

GET https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=2
{
 "messages": [
  {
   "id": "14fe21fd6b3fb46f",
   "threadId": "14fe21fd6b3fb46f"
  },
  {
   "id": "14fe21f9341ed73c",
   "threadId": "14fe21f9341ed73c"
  }
 ],
 "nextPageToken": "08943597140129624594",
 "resultSizeEstimate": 3
}
{
 "payload": {
  "parts": [
   {
    "partId": "0",
    "mimeType": "text/plain",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/plain; charset=UTF-8"
     }
    ],
    "body": {
     "size": 22,
     "data": "aGVjaz8gTm8gYXR0YWNobWVudD8NCg=="
    }
   },
   {
    "partId": "1",
    "mimeType": "text/html",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/html; charset=UTF-8"
     }
    ],
    "body": {
     "size": 43,
     "data": "PGRpdiBkaXI9Imx0ciI-aGVjaz8gTm8gYXR0YWNobWVudD88L2Rpdj4NCg=="
    }
   }
  ]
 }
}
{
 "payload": {
  "parts": [
   {
    "mimeType": "multipart/alternative",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "multipart/alternative; boundary=001a1142e23c551e8e05200b4be0"
     }
    ],
    "body": {
     "size": 0
    },
    "parts": [
     {
      "partId": "0.0",
      "mimeType": "text/plain",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/plain; charset=UTF-8"
       }
      ],
      "body": {
       "size": 9,
       "data": "V293IG1hbg0K"
      }
     },
     {
      "partId": "0.1",
      "mimeType": "text/html",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/html; charset=UTF-8"
       }
      ],
      "body": {
       "size": 30,
       "data": "PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K"
      }
     }
    ]
   },
   {
    "partId": "1",
    "mimeType": "image/jpeg",
    "filename": "feelthebern.jpg",
    "headers": [
     {
      "name": "Content-Type",
      "value": "image/jpeg; name=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Disposition",
      "value": "attachment; filename=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Transfer-Encoding",
      "value": "base64"
     },
     {
      "name": "X-Attachment-Id",
      "value": "f_ieq3ev0i0"
     }
    ],
    "body": {
     "attachmentId": "ANGjdJ_2xG3WOiLh6MbUdYy4vo2VhV2kOso5AyuJW3333rbmk8BIE1GJHIOXkNIVGiphP3fGe7iuIl_MGzXBGNGvNslwlz8hOkvJZg2DaasVZsdVFT_5JGvJOLefgaSL4hqKJgtzOZG9K1XSMrRQAtz2V0NX7puPdXDU4gvalSuMRGwBhr_oDSfx2xljHEbGG6I4VLeLZfrzGGKW7BF-GO_FUxzJR8SizRYqIhgZNA6PfRGyOhf1s7bAPNW3M9KqWRgaK07WTOYl7DzW4hpNBPA4jrl7tgsssExHpfviFL7yL52lxsmbsiLe81Z5UoM",
     "size": 100446
    }
   }
  ]
 }
}
响应:

GET https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=2
{
 "messages": [
  {
   "id": "14fe21fd6b3fb46f",
   "threadId": "14fe21fd6b3fb46f"
  },
  {
   "id": "14fe21f9341ed73c",
   "threadId": "14fe21f9341ed73c"
  }
 ],
 "nextPageToken": "08943597140129624594",
 "resultSizeEstimate": 3
}
{
 "payload": {
  "parts": [
   {
    "partId": "0",
    "mimeType": "text/plain",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/plain; charset=UTF-8"
     }
    ],
    "body": {
     "size": 22,
     "data": "aGVjaz8gTm8gYXR0YWNobWVudD8NCg=="
    }
   },
   {
    "partId": "1",
    "mimeType": "text/html",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/html; charset=UTF-8"
     }
    ],
    "body": {
     "size": 43,
     "data": "PGRpdiBkaXI9Imx0ciI-aGVjaz8gTm8gYXR0YWNobWVudD88L2Rpdj4NCg=="
    }
   }
  ]
 }
}
{
 "payload": {
  "parts": [
   {
    "mimeType": "multipart/alternative",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "multipart/alternative; boundary=001a1142e23c551e8e05200b4be0"
     }
    ],
    "body": {
     "size": 0
    },
    "parts": [
     {
      "partId": "0.0",
      "mimeType": "text/plain",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/plain; charset=UTF-8"
       }
      ],
      "body": {
       "size": 9,
       "data": "V293IG1hbg0K"
      }
     },
     {
      "partId": "0.1",
      "mimeType": "text/html",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/html; charset=UTF-8"
       }
      ],
      "body": {
       "size": 30,
       "data": "PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K"
      }
     }
    ]
   },
   {
    "partId": "1",
    "mimeType": "image/jpeg",
    "filename": "feelthebern.jpg",
    "headers": [
     {
      "name": "Content-Type",
      "value": "image/jpeg; name=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Disposition",
      "value": "attachment; filename=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Transfer-Encoding",
      "value": "base64"
     },
     {
      "name": "X-Attachment-Id",
      "value": "f_ieq3ev0i0"
     }
    ],
    "body": {
     "attachmentId": "ANGjdJ_2xG3WOiLh6MbUdYy4vo2VhV2kOso5AyuJW3333rbmk8BIE1GJHIOXkNIVGiphP3fGe7iuIl_MGzXBGNGvNslwlz8hOkvJZg2DaasVZsdVFT_5JGvJOLefgaSL4hqKJgtzOZG9K1XSMrRQAtz2V0NX7puPdXDU4gvalSuMRGwBhr_oDSfx2xljHEbGG6I4VLeLZfrzGGKW7BF-GO_FUxzJR8SizRYqIhgZNA6PfRGyOhf1s7bAPNW3M9KqWRgaK07WTOYl7DzW4hpNBPA4jrl7tgsssExHpfviFL7yL52lxsmbsiLe81Z5UoM",
     "size": 100446
    }
   }
  ]
 }
}
我只要求有效载荷,因为所有相关部件都在有效载荷下:

fields = payload

GET https://www.googleapis.com/gmail/v1/users/me/messages/14fe21fd6b3fb46f?fields=payload

GET https://www.googleapis.com/gmail/v1/users/me/messages/14fe21f9341ed73c?fields=payload
无附件邮件:

GET https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=2
{
 "messages": [
  {
   "id": "14fe21fd6b3fb46f",
   "threadId": "14fe21fd6b3fb46f"
  },
  {
   "id": "14fe21f9341ed73c",
   "threadId": "14fe21f9341ed73c"
  }
 ],
 "nextPageToken": "08943597140129624594",
 "resultSizeEstimate": 3
}
{
 "payload": {
  "parts": [
   {
    "partId": "0",
    "mimeType": "text/plain",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/plain; charset=UTF-8"
     }
    ],
    "body": {
     "size": 22,
     "data": "aGVjaz8gTm8gYXR0YWNobWVudD8NCg=="
    }
   },
   {
    "partId": "1",
    "mimeType": "text/html",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/html; charset=UTF-8"
     }
    ],
    "body": {
     "size": 43,
     "data": "PGRpdiBkaXI9Imx0ciI-aGVjaz8gTm8gYXR0YWNobWVudD88L2Rpdj4NCg=="
    }
   }
  ]
 }
}
{
 "payload": {
  "parts": [
   {
    "mimeType": "multipart/alternative",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "multipart/alternative; boundary=001a1142e23c551e8e05200b4be0"
     }
    ],
    "body": {
     "size": 0
    },
    "parts": [
     {
      "partId": "0.0",
      "mimeType": "text/plain",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/plain; charset=UTF-8"
       }
      ],
      "body": {
       "size": 9,
       "data": "V293IG1hbg0K"
      }
     },
     {
      "partId": "0.1",
      "mimeType": "text/html",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/html; charset=UTF-8"
       }
      ],
      "body": {
       "size": 30,
       "data": "PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K"
      }
     }
    ]
   },
   {
    "partId": "1",
    "mimeType": "image/jpeg",
    "filename": "feelthebern.jpg",
    "headers": [
     {
      "name": "Content-Type",
      "value": "image/jpeg; name=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Disposition",
      "value": "attachment; filename=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Transfer-Encoding",
      "value": "base64"
     },
     {
      "name": "X-Attachment-Id",
      "value": "f_ieq3ev0i0"
     }
    ],
    "body": {
     "attachmentId": "ANGjdJ_2xG3WOiLh6MbUdYy4vo2VhV2kOso5AyuJW3333rbmk8BIE1GJHIOXkNIVGiphP3fGe7iuIl_MGzXBGNGvNslwlz8hOkvJZg2DaasVZsdVFT_5JGvJOLefgaSL4hqKJgtzOZG9K1XSMrRQAtz2V0NX7puPdXDU4gvalSuMRGwBhr_oDSfx2xljHEbGG6I4VLeLZfrzGGKW7BF-GO_FUxzJR8SizRYqIhgZNA6PfRGyOhf1s7bAPNW3M9KqWRgaK07WTOYl7DzW4hpNBPA4jrl7tgsssExHpfviFL7yL52lxsmbsiLe81Z5UoM",
     "size": 100446
    }
   }
  ]
 }
}
带有附件的邮件:

GET https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=2
{
 "messages": [
  {
   "id": "14fe21fd6b3fb46f",
   "threadId": "14fe21fd6b3fb46f"
  },
  {
   "id": "14fe21f9341ed73c",
   "threadId": "14fe21f9341ed73c"
  }
 ],
 "nextPageToken": "08943597140129624594",
 "resultSizeEstimate": 3
}
{
 "payload": {
  "parts": [
   {
    "partId": "0",
    "mimeType": "text/plain",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/plain; charset=UTF-8"
     }
    ],
    "body": {
     "size": 22,
     "data": "aGVjaz8gTm8gYXR0YWNobWVudD8NCg=="
    }
   },
   {
    "partId": "1",
    "mimeType": "text/html",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "text/html; charset=UTF-8"
     }
    ],
    "body": {
     "size": 43,
     "data": "PGRpdiBkaXI9Imx0ciI-aGVjaz8gTm8gYXR0YWNobWVudD88L2Rpdj4NCg=="
    }
   }
  ]
 }
}
{
 "payload": {
  "parts": [
   {
    "mimeType": "multipart/alternative",
    "filename": "",
    "headers": [
     {
      "name": "Content-Type",
      "value": "multipart/alternative; boundary=001a1142e23c551e8e05200b4be0"
     }
    ],
    "body": {
     "size": 0
    },
    "parts": [
     {
      "partId": "0.0",
      "mimeType": "text/plain",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/plain; charset=UTF-8"
       }
      ],
      "body": {
       "size": 9,
       "data": "V293IG1hbg0K"
      }
     },
     {
      "partId": "0.1",
      "mimeType": "text/html",
      "filename": "",
      "headers": [
       {
        "name": "Content-Type",
        "value": "text/html; charset=UTF-8"
       }
      ],
      "body": {
       "size": 30,
       "data": "PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K"
      }
     }
    ]
   },
   {
    "partId": "1",
    "mimeType": "image/jpeg",
    "filename": "feelthebern.jpg",
    "headers": [
     {
      "name": "Content-Type",
      "value": "image/jpeg; name=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Disposition",
      "value": "attachment; filename=\"feelthebern.jpg\""
     },
     {
      "name": "Content-Transfer-Encoding",
      "value": "base64"
     },
     {
      "name": "X-Attachment-Id",
      "value": "f_ieq3ev0i0"
     }
    ],
    "body": {
     "attachmentId": "ANGjdJ_2xG3WOiLh6MbUdYy4vo2VhV2kOso5AyuJW3333rbmk8BIE1GJHIOXkNIVGiphP3fGe7iuIl_MGzXBGNGvNslwlz8hOkvJZg2DaasVZsdVFT_5JGvJOLefgaSL4hqKJgtzOZG9K1XSMrRQAtz2V0NX7puPdXDU4gvalSuMRGwBhr_oDSfx2xljHEbGG6I4VLeLZfrzGGKW7BF-GO_FUxzJR8SizRYqIhgZNA6PfRGyOhf1s7bAPNW3M9KqWRgaK07WTOYl7DzW4hpNBPA4jrl7tgsssExHpfviFL7yL52lxsmbsiLe81Z5UoM",
     "size": 100446
    }
   }
  ]
 }
}
这些响应对应于代码中的
$parts
。正如您所见,如果您幸运的话,
$parts[0]['body']->数据将为您提供所需,但大多数情况下不会

通常有两种方法来解决这个问题。您可以实现以下算法(您在PHP方面比我好得多,但这是它的概述):

  • 遍历
    payload.parts
    ,检查它是否包含一个
    part
    ,该part具有您要查找的正文(或者
    text/plain
    或者
    text/html
    )。如果有,您就完成了搜索。如果你正在解析一封没有附件的邮件,这就足够了
  • 再次执行步骤1,但这一次使用您刚刚检查的
    部分中的
    部分
    ,递归执行。您最终将找到您的
    零件
    。如果您正在解析一封带有附件的邮件,这将最终找到您的
    正文
  • 该算法可能类似于以下内容(JavaScript中的示例):

    var响应={
    “有效载荷”:{
    “部分”:[
    {
    “mimeType”:“多部分/备选方案”,
    “文件名”:“,
    “标题”:[
    {
    “名称”:“内容类型”,
    “值”:“多部分/备选方案;边界=001a1142e23c551e8e05200b4be0”
    }
    ],
    “正文”:{
    “大小”:0
    },
    “部分”:[
    {
    “partId”:“0.0”,
    “mimeType”:“文本/普通”,
    “文件名”:“,
    “标题”:[
    {
    “名称”:“内容类型”,
    “值”:“文本/纯文本;字符集=UTF-8”
    }
    ],
    “正文”:{
    “尺寸”:9,
    “数据”:“V293IG1hbg0K”
    }
    },
    {
    “partId”:“0.1”,
    “mimeType”:“text/html”,
    “文件名”:“,
    “标题”:[
    {
    “名称”:“内容类型”,
    “值”:“text/html;charset=UTF-8”
    }
    ],
    “正文”:{
    “尺寸”:30,
    “数据”:“PGRpdiBkaXI9Imx0ciI-V293IG1hbjwvZGl2Pg0K”
    }
    }
    ]
    },
    {
    “partId”:“1”,
    “mimeType”:“图像/jpeg”,
    “文件名”:“feelthebern.jpg”,
    “标题”:[
    {
    “名称”:“内容类型”,
    “值”:“image/jpeg;name=\“feelthebern.jpg”
    },
    {
    “名称”:“内容处置”,
    “值”:“附件;文件名=\”feelthebern.jpg“”
    },
    {
    “名称”:“内容传输编码”,
    “值”:“base64”
    },
    {
    “名称”:“X-附件-Id”,
    “值”:“f_ieq3ev0i0”
    }
    ],
    “正文”:{
    “附件”是一个附件:“附件”是一个附件,它是一个附件,它是一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个附件,一个,一个附件,一个附件,一个附件,一个,一个附件,一个附件,一个,一个,一个,一个,一个,一5计量单位“,
    “尺寸”:100446
    }
    }
    ]
    }
    };
    //例如,在纯文本消息中,有效负载是唯一的部分。
    var parts=[response.payload];
    while(零件长度){
    var part=parts.shift();
    如果(部分,部分){
    零件=零件-混凝土(零件-零件);
    }
    如果(part.mimeType==='text/html'){
    var decodedPart=decodeURIComponent(escape(atob(part.body.data.replace(/\-/g,'+')).replace(/\\-/g,'/')));
    console.log(解码部分);
    }
    }
    更新:您可能想查看下面我的第二个答案,以获得更完整的代码。

    最后,我今天终于工作了,下面是找到尸体的完整代码答案——多亏了@Tholle

    // Authentication things above
    /*
     * Decode the body.
     * @param : encoded body  - or null
     * @return : the body if found, else FALSE;
     */
    function decodeBody($body) {
        $rawData = $body;
        $sanitizedData = strtr($rawData,'-_', '+/');
        $decodedMessage = base64_decode($sanitizedData);
        if(!$decodedMessage){
            $decodedMessage = FALSE;
        }
        return $decodedMessage;
    }
    
    $client = getClient();
    $gmail = new Google_Service_Gmail($client);
    
    $list = $gmail->users_messages->listUsersMessages('me', ['maxResults' => 1000]);
    
    try{
        while ($list->getMessages() != null) {
    
            foreach ($list->getMessages() as $mlist) {
    
                $message_id = $mlist->id;
                $optParamsGet2['format'] = 'full';
                $single_message = $gmail->users_messages->get('me', $message_id, $optParamsGet2);
                $payload = $single_message->getPayload();
    
                // With no attachment, the payload might be directly in the body, encoded.
                $body = $payload->getBody();
                $FOUND_BODY = decodeBody($body['data']);
    
                // If we didn't find a body, let's look for the parts
                if(!$FOUND_BODY) {
                    $parts = $payload->getParts();
                    foreach ($parts  as $part) {
                        if($part['body']) {
                            $FOUND_BODY = decodeBody($part['body']->data);
                            break;
                        }
                        // Last try: if we didn't find the body in the first parts, 
                        // let's loop into the parts of the parts (as @Tholle suggested).
                        if($part['parts'] && !$FOUND_BODY) {
                            foreach ($part['parts'] as $p) {
                                // replace 'text/html' by 'text/plain' if you prefer
                                if($p['mimeType'] === 'text/html' && $p['body']) {
                                    $FOUND_BODY = decodeBody($p['body']->data);
                                    break;
                                }
                            }
                        }
                        if($FOUND_BODY) {
                            break;
                        }
                    }
                }
                // Finally, print the message ID and the body
                print_r($message_id . " : " . $FOUND_BODY);
            }
    
            if ($list->getNextPageToken() != null) {
                $pageToken = $list->getNextPageToken();
                $list = $gmail->users_messages->listUsersMessages('me', ['pageToken' => $pageToken, 'maxResults' => 1000]);
            } else {
                break;
            }
        }
    } catch (Exception $e) {
        echo $e->getMessage();
    }
    
    如您所见,我的问题是,有时车身无法在有效载荷->零件中找到,而是直接在有效载荷->车身中找到!(另外,我为多个零件添加了循环)


    希望这对其他人有所帮助。

    对于那些感兴趣的人,我极大地改进了我的上一个答案,使其能够使用text/html(并在必要时回退到text/plain),并将图像转换为base64附件,在打印为完整html时自动加载

    代码一点也不完美,而且太长,无法详细解释,但它对我来说很有用

    随时接受并调整它(如有必要,可能纠正/改进它)

    //上面的认证内容
    /*
    *解码尸体。
    *@param:encoded body-或null
    *@return:找到尸体,否则为假;
    */
    函数体($body){
    $rawData=$body;
    $sanitizedData=strtr($rawData,'-'','+/');
    $decodedMessage=base64_decode($sanitizedData);
    如果(!$decodedMessage){
    $decodedMessage=FALSE;
    }
    返回$decodedMessage;
    }
    $client=getClient();
    $gmail=新的谷歌服务\ gmail($client);
    $list=$gmail->users\u messages->listUsersMessages('me',['maxResults'=>1000]);
    试一试{
    而($list->getMessages()!=null){
    foreach($list->getMessages()作为$mlist){
    $message_id=$mlist->id;
    $optParamsGet2['format']='full';
    $single_message=$gmail->users_messages->get('me',$message_id,$optParamsGet2);
    $payload=$single_message->getPayload();
    $parts=$payload->getParts();
    //没有附件,t