I'm trying to send 2K emails to my customers. I'm using Amazon SES to send the email using this library. When I try to send the email with the attachment, I'm getting SimpleEmailService::sendEmail(): 35 Process open FD table is full error. I just followed the instruction given in the readme file. And changed credentials only. Any help will be appreciated... :)
Edit
Almost 900 emails with the attachment sent successfully. But after that, it starts throwing the error.
The attachment is of type PDF.
Edit 2
The service team says that you are not closing the file descriptor after sending the mails. You need to close the file descriptor after the job is done. That way it wont exhaust the limit set on the number of FDs assigned
Here is the code I'm trying.
$mails = [
'customer#email.com'
...
...
...
];
$ses = new SimpleEmailService('XXXXXXX', 'XXXXXXX');
$ses->enableVerifyPeer(false);
$m = new SimpleEmailServiceMessage();
foreach ($mails as $email) {
try {
$m->setFrom('From <example#email.com>');
$m->setSubject('Subject');
$m->setMessageFromString('','<p>This is the test email.</p>');
$m->addTo($email);
$atch = path to pdf;
$mime_type = #mime_content_type($atch);
$tmp = str_replace('\\','/', $atch);
$file_name = basename($tmp);
$m->addAttachmentFromFile($file_name, $atch, $mime_type);
$response = $ses->sendEmail($m,false,true);
$m->clearRecipients();
$m->attachments = [];
} catch (Exception $ex) {
echo $ex->getMessage();
}
}
Solved
Just added $ses->setBulkMode(true); after $ses->enableVerifyPeer(false);. ..:)
Related
I need to be able to send one, maybe more, files stored on an Amazon S3 server as attachments in an email created using SendGrid.
The problem I have is that I'm not a web dev expert and the sparse PHP examples I can find are not helping me much.
Do I need to download the files from the S3 server to the local /tmp directory and add them as attachments that way, or can I pass the body of the file from the FileController and insert it as an attachment that way?
I'm not really sure where to start, but here's what I've done so far:
$attachments = array();
// Process the attachment_ids
foreach($attachment_ids as $attachment_id) {
// Get the file if it is attached to the Activity
if (in_array($attachment_id, $activity_file_ids)) {
$file = File::find($attachment_id);
$fileController = new FileController($this->_app);
$fileObject = $fileController->getFile($attachment_id);
error_log(print_r($fileObject, true));
$attachment = array();
$attachment['content'] = $fileObject;
$attachment['type'] = $fileController->mime_content_type($file->file_ext);
$attachment['name'] = explode(".", $file->filename, 2)[0];
$attachment['filename'] = $file->filename;
$attachment['disposition'] = "inline";
$attachment['content_id'] = '';
}
}
My next step would be to push the $attachment array to the $attachments array. Once $attachments is complete, iterate through it and add each $attachment to the SendGrid e-mail object (the e-mail is working fine without attachments, btw.)
Problem is, I'm not sure I'm going down the right road with this or if there's a much shorter and neater (and working) way of doing it?
FileController->getFile() essentially does this:
$file = $this->_s3->getObject(array(
'Bucket' => $bucket,
'Key' => $filename,
));
return $file['Body'];
Any help (especially code examples) would be greatly appreciated!
Okay, I've got a working solution to this now - here's the code:
// Process the attachment_ids
foreach($attachment_ids as $attachment_id) {
// Get the file if it is attached to the Activity
if (in_array($attachment_id, $activity_file_ids)) {
// Get the file record
$file = File::find($attachment_id);
// Get an instance of FileController
$fileController = new FileController($this->_app);
// Set up the Attachment object
$attachment = new \SendGrid\Attachment();
$attachment->setContent(base64_encode($fileController->getFile($attachment_id)));
$attachment->setType($fileController->mime_content_type($file->file_ext));
$attachment->setFilename($file->filename);
$attachment->setDisposition("attachment");
$attachment->setContentId($file->file_desc);
// Add the attachment to the mail
$mail->addAttachment($attachment);
}
}
Don't know if it will help anybody else, but there it is. The solution was to get the file from the S3 server and pass base64_encode($file['Body']) to the setContent function of an instantiated Attachment object, along with setting a few other fields for it too.
in the official GAE PHP Mail Api doc https://cloud.google.com/appengine/docs/php/mail/ they show this example:
use \google\appengine\api\mail\Message;
// Notice that $image_data is the raw file data of the attachment.
try
{
$message = new Message();
$message->setSender("from#google.com");
$message->addTo("to#google.com");
$message->setSubject("Example email");
$message->setTextBody("Hello, world!");
$message->addAttachment('image.jpg', $image_data, $image_content_id);
$message->send();
} catch (InvalidArgumentException $e) {
// ...
}
but they dont explain how to fill $image_data with an uploaded static file.
any help ? if it can be explicit it will be great
thanks
diego
Use file_get_contents() to retrieve the data you want to send as part of the email.
$image_data = file_get_contents('path/to/static/file.jpg');
Or you can send an image stored in Cloud Storage
$image_data = file_get_contents('gs://my_bucket/path/to/file.jpg');
im generating a daily report from a php script, it is residing in a folder as follows
report_2015_02_15.csv
report_2015_02_16.csv
report_2015_02_17.csv
report_2015_02_18.csv
report_2015_02_19.csv
And im sending my users an email with link to download, once they click the download link the download script triggers and prepares the download. It currently gets all the files into an array sorts it and finds the latest for the download,
this method has a flow in it where, even when you go to a email that is 2 weeks older and clicks the download link, it gives you the latest report instead of giving the two weeks older report.
so can anybody tell me a way to send the download link in my email with a relationship to its corresponding file?
email script
$down_link = Config_Reader::readProjectConfig('tad')->base_url.'CSVDownload.php;
$mail = new Zend_Mail ();
$sentFromEmail = $config->sentFromrec;
$tr = new Zend_Mail_Transport_Sendmail ( '-f' . $sentFromEmail );
Zend_Mail::setDefaultTransport ( $tr );
$mail->setReturnPath($sentFromEmail);
$mail->setFrom ( $sentFromEmail, 'Reporting' );
$email_body = "Hi,<br /><br />Download the weekly details of adtracker projects, by clicking the link below.
<br /> Report<br /><br />Thank You.";
$mail->setBodyHtml ( $email_body );
$mail->addTo ( $weeklyReportRec);
$mail->setSubject ( "Report" );
try {
$mail->send ();
} catch ( Exception $e ) {
echo "Mail sending failed.\n";
}
download script
$basePath = "$download/dailyReport/";
$csvFiles = glob($basePath."/*.csv");
if(count($csvFiles)){
array_multisort($csvFiles, SORT_DESC);
$csvFile = $csvFiles[0];
}else{
exit("Server error!");
}
$h = fopen($csvFile, "r");
$contents = fread($h, filesize($csvFile));
You can use a query parameter indicating the report date on the download link. .../CSCDownload.php?date=2015/02/17
This will allow you to select the respective report from the available list.
You can use somthing like that as download script. I made just minum changes to get it work:
$date = $_GET['date'];
$basePath = "$download/dailyReport/";
$csvFiles = glob($basePath."/*.csv");
$file = $basePath . 'report_' . $date . '.csv';
$found = false;
foreach($csvFiles as $csvFile){
if($file === $csvFile){
$found = $csvFile;
// You have found the file, so you can stop searching
break;
}
}
if(false === $found){
exit("Server error!");
}
$h = fopen($found, "r");
$contents = fread($h, filesize($found));
Now you can call you script in your browser with "example.com/getcsvfile.php?date=2015_02_15" without worrying about injection, because you check if the file is one of the csv files.
I have set up an windows azure website (php), and I want to connect to the azure storage (blob) environment. I walked through the How to use the Blob service from PHP tutorial, but that only mentions the case when the website is stored localy.
I tried to set up a few cases, but i'm constantly getting a http 500 error.
<?php
require_once 'WindowsAzure/WindowsAzure.php';
use WindowsAzure\Common\ServicesBuilder;
use WindowsAzure\Common\ServiceException;
//$connectionString = "\WindowsAzure\Blob\Internal\IBlob";
// Create blob REST proxy.
$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($connectionString); // the code gets stuck at this line, result is a HTTP 500 error
$content = fopen("C:\Users\Public\Pictures\Sample%20Pictures\Woestijn.jpg", "r");
$blob_name = "newBlob";
try {
//Upload blob
$blobRestProxy->createBlockBlob("default", $blob_name, $content);
}
catch(ServiceException $e){
// Handle exception based on error codes and messages.
// Error codes and messages are here:
// http://msdn.microsoft.com/en-us/library/windowsazure/dd179439.aspx
$code = $e->getCode();
$error_message = $e->getMessage();
echo $code.": ".$error_message."<br />";
}?>
Is there anyone who had a similar problem and managed to figure it out?
EDIT:
I now narrowed down the error search. I went into the ServicesBuilder.php file, and commented out line by line, until the page stopped to work. The line it went wrong at is $httpClient, as shown below:
public function createBlobService($connectionString)
{
$settings = StorageServiceSettings::createFromConnectionString(
$connectionString
);
$httpClient = $this->httpClient();
$serializer = $this->serializer();
$uri = Utilities::tryAddUrlScheme(
$settings->getBlobEndpointUri()
);
From what I'm seeing you're filling up the $connectionString variable with this value: "\WindowsAzure\Blob\Internal\IBlob" (even though it's commented - so probably you're passing it from somewhere else). If that's the case you'll need to change it.
The connection string should be a reference to your storage account containing the protocol, the name of the account and the key (you can find the name and the key in the portal):
$connectionString = "DefaultEndpointsProtocol=https;AccountName=jeroensaccount;AccountKey=fjmezfjmIOFJEZIOPAFJAZOPIFJAIMO"
i am developing a site in which users can mail tickets and attach any type of files to a specific mail id. I need to add the mail subject, content and attachment to the database. I am doing this using cron. Except the attachments every thing works perfect. I have seen some post which create download links. Since i am using cron i can't do it manually.
$hostname = '{xxxx.net:143/novalidate-cert}INBOX';
$username = 'yyy#xxxx.net';
$password = 'zzzz';
/* try to connect */
$inbox = imap_open($hostname,$username,$password) or die('Cannot connect to : ' . imap_last_error());
$emails = imap_search($inbox,'ALL');
if($emails) {
$output = '';
rsort($emails);
foreach($emails as $email_number) {
$structure = imap_fetchstructure($inbox, $email_number);
$name = $structure->parts[1]->dparameters[0]->value; // name of the file
$type = $structure->parts[1]->type; //type of the file
}}
I am able to get type and name of the files but don't know how to proceed further
Any one please help me. thank you...
To save attachments as files, you need to parse the structure of the message and take out all parts that are attachments on it's own (content disposition). You should wrap that into classes of their own so you have an easy access and you can handle errors more easily over time, email parsing can be fragile:
$savedir = __DIR__ . '/imap-dump/';
$inbox = new IMAPMailbox($hostname, $username, $password);
$emails = $inbox->search('ALL');
if ($emails) {
rsort($emails);
foreach ($emails as $email) {
foreach ($email->getAttachments() as $attachment) {
$savepath = $savedir . $attachment->getFilename();
file_put_contents($savepath, $attachment);
}
}
}
The code of these classes is more or less wrapping the imap_... functions, but for the attachment classes, it's doing the parsing of the structures as well. You find the code on github. Hope this is helpful.
Although using PHP + Cron and a standard mail server might work, the amount of work needed to handle all the edge cases, error reporting, etc might not be worth the time. Although I haven't used it, Postmark Inbound seems like an incredible (paid) service that will eliminate most of the headache of processing email via the PHP imap api.
If you want to try to handle everything via PHP, you might want to check this resource out.
In case if you want to download attachments as zip
$zip = new ZipArchive();
$tmp_file = tempnam('.', '');
$zip->open($tmp_file, ZipArchive::CREATE);
$mailbox = $connection->getMailbox('INBOX');
foreach ($mailbox->getMessage() as $message) {
$attachments = $message->getAttachments();
foreach ($attachments as $attachment) {
$zip->addFromString($attachment->getFilename(), $attachment->getDecodedContent());
}
}
$zip->close();
# send the file to the browser as a download
header('Content-disposition: attachment; filename=download.zip');
header('Content-type: application/zip');
readfile($tmp_file);
This code uses library hosted on GitHub. Hope this is helpful.