I\'m using the PHP SDK for the new Gmail API. How do I fetch an attachment from an email?
Here is the API documentation, but in this instance, it\'s missing example
Try the code in this thread if it works on you.
<?php
/**
* Gmail attachment extractor.
*
* Downloads attachments from Gmail and saves it to a file.
* Uses PHP IMAP extension, so make sure it is enabled in your php.ini,
* extension=php_imap.dll
*
*/
set_time_limit(3000);
/* connect to gmail with your credentials */
$hostname = '{imap.gmail.com:993/imap/ssl}INBOX';
$username = 'YOUR_GMAIL_USERNAME'; # e.g somebody@gmail.com
$password = 'YOUR_GMAIL_PASSWORD';
/* try to connect */
$inbox = imap_open($hostname,$username,$password) or die('Cannot connect to Gmail: ' . imap_last_error());
/* get all new emails. If set to 'ALL' instead
* of 'NEW' retrieves all the emails, but can be
* resource intensive, so the following variable,
* $max_emails, puts the limit on the number of emails downloaded.
*
*/
$emails = imap_search($inbox,'ALL');
/* useful only if the above search is set to 'ALL' */
$max_emails = 16;
/* if any emails found, iterate through each email */
if($emails) {
$count = 1;
/* put the newest emails on top */
rsort($emails);
/* for every email... */
foreach($emails as $email_number)
{
/* get information specific to this email */
$overview = imap_fetch_overview($inbox,$email_number,0);
/* get mail message, not actually used here.
Refer to http://php.net/manual/en/function.imap-fetchbody.php
for details on the third parameter.
*/
$message = imap_fetchbody($inbox,$email_number,2);
/* get mail structure */
$structure = imap_fetchstructure($inbox, $email_number);
$attachments = array();
/* if any attachments found... */
if(isset($structure->parts) && count($structure->parts))
{
for($i = 0; $i < count($structure->parts); $i++)
{
$attachments[$i] = array(
'is_attachment' => false,
'filename' => '',
'name' => '',
'attachment' => ''
);
if($structure->parts[$i]->ifdparameters)
{
foreach($structure->parts[$i]->dparameters as $object)
{
if(strtolower($object->attribute) == 'filename')
{
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['filename'] = $object->value;
}
}
}
if($structure->parts[$i]->ifparameters)
{
foreach($structure->parts[$i]->parameters as $object)
{
if(strtolower($object->attribute) == 'name')
{
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['name'] = $object->value;
}
}
}
if($attachments[$i]['is_attachment'])
{
$attachments[$i]['attachment'] = imap_fetchbody($inbox, $email_number, $i+1);
/* 3 = BASE64 encoding */
if($structure->parts[$i]->encoding == 3)
{
$attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
}
/* 4 = QUOTED-PRINTABLE encoding */
elseif($structure->parts[$i]->encoding == 4)
{
$attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
}
}
}
}
/* iterate through each attachment and save it */
foreach($attachments as $attachment)
{
if($attachment['is_attachment'] == 1)
{
$filename = $attachment['name'];
if(empty($filename)) $filename = $attachment['filename'];
if(empty($filename)) $filename = time() . ".dat";
/* prefix the email number to the filename in case two emails
* have the attachment with the same file name.
*/
$fp = fopen("./" . $email_number . "-" . $filename, "w+");
fwrite($fp, $attachment['attachment']);
fclose($fp);
}
}
if($count++ >= $max_emails) break;
}
}
/* close the connection */
imap_close($inbox);
echo "Done";
?>
For more information, check this related threads and SO question
Can't open downloaded attachments from Gmail API
Extracting Attachments From Emails With PHP
Gmail fetch attachment
Firstly we need to get the data from the attachment object:
$attachmentObj = $service->users_messages_attachments->get($emailAccount, $messageId, $attachmentId);
$data = $attachmentObj->getData(); //Get data from attachment object
Then before writing to file, convert the data to standard RFC 4648 base64-encoding:
$data = strtr($data, array('-' => '+', '_' => '/'));
$myfile = fopen("excel.xlsx", "w+");;
fwrite($myfile, base64_decode($data));
fclose($myfile);
It now works!
function getAttachment($messageId, $partId, $userId)
{
try {
$client = getClient();
$gmail = new Google_Service_Gmail($client);
$message = $gmail->users_messages->get($userId, $messageId);
$message_payload_details = $message->getPayload()->getParts();
$attachmentDetails = array();
$attachmentDetails['attachmentId'] = $message_payload_details[$partId]['body']['attachmentId'];
$attachmentDetails['headers'] = $message_payload_details[$partId]['headers'];
$attachment = $gmail->users_messages_attachments->get($userId, $messageId, $attachmentDetails['attachmentId']);
$attachmentDetails['data'] = $attachment->data;
return ['status' => true, 'data' => $attachmentDetails];
} catch (\Google_Service_Exception $e) {
return ['status' => false, 'message' => $e->getMessage()];
}
}
function base64_to_jpeg($base64_string, $content_type) {
$find = ["_","-"]; $replace = ["/","+"];
$base64_string = str_replace($find,$replace,$base64_string);
$url_str = 'data:'.$content_type.','.$base64_string;
$base64_string = "url(".$url_str.")";
$data = explode(',', $base64_string);
return base64_decode( $data[ 1 ] );
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
$opt_param = array();
$opt_param['labelIds'] = 'INBOX';
$opt_param['maxResults'] = 1;
$messages = $service->users_messages->listUsersMessages($userId, $opt_param);
foreach ($messages as $message_thread) {
$message = $service->users_messages->get($userId, $message_thread['id']);
$message_parts = $message->getPayload()->getParts();
$files = array();
$attachId = $message_parts[1]['body']['attachmentId'];
$attach = $service->users_messages_attachments->get($userId, $message['id'], $attachId);
foreach ($message_parts as $key => $value) {
if ( isset($value->body->attachmentId) && !isset($value->body->data)) {
array_push($files, $value['partId']);
}
}
}
if(isset($_GET['messageId']) && $_GET['part_id']){ // This is After Clicking an Attachment
$attachment = getAttachment($_GET['messageId'], $_GET['part_id'], $userId);
$content_type = "";
foreach ($attachment['data']['headers'] as $key => $value) {
if($value->name == 'Content-Type'){ $content_type = $value->value; }
header($value->name.':'.$value->value);
}
$content_type_val = current(explode("/",$content_type));
$media_types = ["video", "image", "application"];
if(in_array($content_type_val, $media_types )){
echo base64_to_jpeg($attachment['data']['data'], $content_type); // Only for Image files
} else {
echo base64_decode($attachment['data']['data']); // Other than Image Files
}
} else { // Listing All Attachments
if(!empty($files)) {
foreach ($files as $key => $value) {
echo '<a target="_blank" href="index.php?messageId='.$message['id'].'&part_id='.$value.'">Attachment '.($key+1).'</a><br/>';
}
}
}
maybe someone else will be searching for the simple solution. Please see below script code that can be used as Google Script (Java Script, see start tutorial here: https://script.google.com/home/start ):
// GLOBALS
//Array of file extension which you would like to extract to Drive
var fileTypesToExtract = ['sql','gz'];
//Name of the folder in google drive i which files will be put
var folderName = 'BACKUPS';
//Name of the label which will be applied after processing the mail message
var labelName = 'SavedToGDrive';
function GmailToDrive(){
//build query to search emails
var query = '';
//uncomment to search by filetypes
//filename:jpg OR filename:tif OR filename:gif OR fileName:png OR filename:bmp OR filename:svg'; //'after:'+formattedDate+
//for(var i in fileTypesToExtract){
// query += (query === '' ?('filename:'+fileTypesToExtract[i]) : (' OR filename:'+fileTypesToExtract[i]));
//}
//query = 'in:inbox has:nouserlabels ' + query;
//search by label
query = 'label:backups-real-estate-backup';
//exclude already downloaded mails, so you can repeat running this script
if timeout happened
query = query + ' AND NOT label:savedtogdrive';
var threads = GmailApp.search(query);
var label = getGmailLabel_(labelName);
var parentFolder;
if(threads.length > 0){
parentFolder = getFolder_(folderName);
}
var root = DriveApp.getRootFolder();
for(var i in threads){
var mesgs = threads[i].getMessages();
for(var j in mesgs){
//get attachments
var attachments = mesgs[j].getAttachments();
for(var k in attachments){
var attachment = attachments[k];
var isDefinedType = checkIfDefinedType_(attachment);
if(!isDefinedType) continue;
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
parentFolder.addFile(file);
root.removeFile(file);
}
}
threads[i].addLabel(label);
}
}
//This function will get the parent folder in Google drive
function getFolder_(folderName){
var folder;
var fi = DriveApp.getFoldersByName(folderName);
if(fi.hasNext()){
folder = fi.next();
}
else{
folder = DriveApp.createFolder(folderName);
}
return folder;
}
//getDate n days back
// n must be integer
function getDateNDaysBack_(n){
n = parseInt(n);
var date = new Date();
date.setDate(date.getDate() - n);
return Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy/MM/dd');
}
function getGmailLabel_(name){
var label = GmailApp.getUserLabelByName(name);
if(!label){
label = GmailApp.createLabel(name);
}
return label;
}
//this function will check for filextension type.
// and return boolean
function checkIfDefinedType_(attachment){
var fileName = attachment.getName();
var temp = fileName.split('.');
var fileExtension = temp[temp.length-1].toLowerCase();
if(fileTypesToExtract.indexOf(fileExtension) !== -1) return true;
else return false;
}
Thanks.