Email Attachments issue
While learning how to add file attachments to email, we faced an issue for a client in Magento 2.
The attachments failed to load, and after investigation, we found the problem was caused by updates in the Zend module.
If you’re looking to send order attachments, you can check the Magento 2 Order Attachment Extension.
Resolution
We created the SendEmailWithAttachment controller using Magento’s core mail transport builder (Magento\Framework\Mail\Template\TransportBuilder) to attach files and send emails efficiently.
<?php
/**
* Webkul Software.
*
* @category Webkul Software Private Limited
* @package Webkul_Email
* @author Webkul Software Private Limited
* @copyright Webkul Software Private Limited (https://webkul.com)
* @license https://store.webkul.com/license.html
*/
namespace Webkul\Email\Controller;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Filesystem\Driver\File;
use Magento\Framework\File\Mime;
use Laminas\Mime\Mime as LaminasMime;
use Laminas\Mime\Part;
use Laminas\Mime\Message as MimeMessage;
class SendEmailWithAttachment extends \Magento\Framework\App\Action\Action
{
/**
* @var \Magento\Framework\Translate\Inline\StateInterface
*/
protected $_inlineTranslation;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var TransportBuilder
*/
protected $transportBuilder;
/**
* @var StoreManagerInterface
*/
protected $storeManager;
/**
* @var ScopeConfigInterface
*/
protected $scopeConfig;
/**
* @var File
*/
protected $fileDriver;
/**
* @var Mime
*/
protected $mime;
/**
* Constructor
*
* @param \Magento\Framework\App\Action\Context $context
* @param Filesystem $filesystem
* @param TransportBuilder $transportBuilder
* @param StoreManagerInterface $storeManager
* @param ScopeConfigInterface $scopeConfig
* @param File $fileDriver
* @param Mime $mime
* @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
*/
public function __construct(
\Magento\Framework\App\Action\Context $context,
Filesystem $filesystem,
TransportBuilder $transportBuilder,
StoreManagerInterface $storeManager,
ScopeConfigInterface $scopeConfig,
File $fileDriver,
Mime $mime,
\Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
) {
parent::__construct($context);
$this->filesystem = $filesystem;
$this->transportBuilder = $transportBuilder;
$this->storeManager = $storeManager;
$this->scopeConfig = $scopeConfig;
$this->fileDriver = $fileDriver;
$this->mime = $mime;
$this->_inlineTranslation = $inlineTranslation;
}
This class is responsible for sending an email with an attached file. We’ll start by adding a function that attaches a file to the email, following the example provided in the Laminas documentation.
Create File Attachment
Our method below is similar; it will take a file attachment, set its properties, and return a Laminas\Mime\Part class.
/**
* Add an attachment to the message inside the transport builder.
*
* @param TransportInterface $transport
* @param string $filePath
*
* @return TransportInterface
*/
protected function addAttachment($transport, $filePath)
{
$fileMimeType = $this->mime->getMimeType($filePath);
$fileContent = $this->fileDriver->fileGetContents($filePath);
$html = $transport->getMessage()->getBody()->generateMessage();
$decodedHtml = quoted_printable_decode($html);
$message = $transport->getMessage();
if (!$message) {
throw new \Magento\Framework\Exception\LocalizedException(__('Email message is null.'));
}
$attachment = new Part($fileContent);
$attachment->disposition = LaminasMime::DISPOSITION_ATTACHMENT;
$attachment->encoding = LaminasMime::MULTIPART_RELATED;
$attachment->filename = basename($filePath);
$attachment->type = $fileMimeType;
$htmlPart = new Part($decodedHtml);
$htmlPart->type = 'text/html';
$htmlPart->charset = 'utf-8';
$htmlPart->encoding = LaminasMime::ENCODING_QUOTEDPRINTABLE;
$mimeMessage = new MimeMessage();
$mimeMessage->setParts([$htmlPart, $attachment]);
$message->setBody($mimeMessage);
return $transport;
}
Add File Attachment to the TransportBuilder
In this code, we’ll attach a file to the transport interface, similar to adding extra content to an email.
The method will accept Laminas\Mime\Part and Magento\Framework\Mail\TransportInterface as parameters.
So what we have down here is to break the example in the Laminas doc into two separate parts.
The final step is to create a method that will send an email to the recipient while attaching the file.
/**
* Function to send email to recipient with attachment
*/
public function execute()
{
$directory = $this->filesystem >getDirectoryWrite(DirectoryList::PUB);
$fileName = 'xyz.csv';
$filePath = $directory->getAbsolutePath($fileName);
$fileContent = $this->fileDriver->fileGetContents($filePath);
$fileMimeType = $this->mime->getMimeType($filePath);
$storeId = $this->storeManager->getStore()->getId();
$senderEmail = $this->scopeConfig->getValue('trans_email/ident_support/email',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
$senderName = $this->scopeConfig->getValue('trans_email/ident_support/name',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
$recipientEmail = 'Receiver email id';
$subject = 'Email subject';
$recipientName = 'Receiver name';
$bodyText = 'Body Content';
$bodyText = nl2br(htmlspecialchars_decode($bodyText, ENT_QUOTES));
$templateVariables = [
"message_email_subject" => $subject,
"message_email_content" => $bodyText,
"recipient_name" => $recipientName,
];
// Build email
$this->_inlineTranslation->suspend();
$transport = $this->transportBuilder->setTemplateIdentifier('your_template_name')
->setTemplateOptions([
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'store' => $storeId,
])
->setTemplateVars($templateVariables)
->setFrom(['email' => $senderEmail, 'name' => $senderName])
->addTo($recipientEmail)
->getTransport();
if ($transport && $transport->getMessage()) {
$transport = $this->addAttachment($transport, $filePath);
$transport->sendMessage();
}
$this->_inlineTranslation->resume();
}
Here is the full code of the Controller SendEmailWithAttachment to add File Attachments to emails in Magento 2.
<?php
/**
* Webkul Software.
*
* @category Webkul Software Private Limited
* @package Webkul_Email
* @author Webkul Software Private Limited
* @copyright Webkul Software Private Limited (https://webkul.com)
* @license https://store.webkul.com/license.html
*/
namespace Webkul\Email\Controller;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Filesystem\Driver\File;
use Magento\Framework\File\Mime;
use Laminas\Mime\Mime as LaminasMime;
use Laminas\Mime\Part;
use Laminas\Mime\Message as MimeMessage;
class SendEmailWithAttachment extends \Magento\Framework\App\Action\Action
{
/**
* @var \Magento\Framework\Translate\Inline\StateInterface
*/
protected $_inlineTranslation;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var TransportBuilder
*/
protected $transportBuilder;
/**
* @var StoreManagerInterface
*/
protected $storeManager;
/**
* @var ScopeConfigInterface
*/
protected $scopeConfig;
/**
* @var File
*/
protected $fileDriver;
/**
* @var Mime
*/
protected $mime;
/**
* Constructor
*
* @param \Magento\Framework\App\Action\Context $context
* @param Filesystem $filesystem
* @param TransportBuilder $transportBuilder
* @param StoreManagerInterface $storeManager
* @param ScopeConfigInterface $scopeConfig
* @param File $fileDriver
* @param Mime $mime
* @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
*/
public function __construct(
\Magento\Framework\App\Action\Context $context,
Filesystem $filesystem,
TransportBuilder $transportBuilder,
StoreManagerInterface $storeManager,
ScopeConfigInterface $scopeConfig,
File $fileDriver,
Mime $mime,
\Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
) {
parent::__construct($context);
$this->filesystem = $filesystem;
$this->transportBuilder = $transportBuilder;
$this->storeManager = $storeManager;
$this->scopeConfig = $scopeConfig;
$this->fileDriver = $fileDriver;
$this->mime = $mime;
$this->_inlineTranslation = $inlineTranslation;
}
/**
* Function to send email to recipient with attachment
*/
public function execute()
{
$varDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::PUB);
$fileName = 'xyz.csv';
$filePath = $varDirectory->getAbsolutePath($fileName);
$fileContent = $this->fileDriver->fileGetContents($filePath);
$fileMimeType = $this->mime->getMimeType($filePath);
$storeId = $this->storeManager->getStore()->getId();
$senderEmail = $this->scopeConfig->getValue('trans_email/ident_support/email',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
$senderName = $this->scopeConfig->getValue('trans_email/ident_support/name',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
$recipientEmail = 'Receiver email id';
$subject = 'Email subject';
$recipientName = 'Receiver name';
$bodyText = 'Body Content';
$bodyText = nl2br(htmlspecialchars_decode($bodyText, ENT_QUOTES));
$templateVariables = [
"message_email_subject" => $subject,
"message_email_content" => $bodyText,
"recipient_name" => $recipientName,
];
// Build emailImage
$this->_inlineTranslation->suspend();
$transport = $this->transportBuilder->setTemplateIdentifier('your_template_name')
->setTemplateOptions([
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'store' => $storeId,
])
->setTemplateVars($templateVariables)
->setFrom(['email' => $senderEmail, 'name' => $senderName])
->addTo($recipientEmail)
->getTransport();
if ($transport && $transport->getMessage()) {
$transport = $this->addAttachment($transport, $filePath);
$transport->sendMessage();
}
$this->_inlineTranslation->resume();
}
/**
* Add an attachment to the message inside the transport builder.
*
* @param TransportInterface $transport
* @param array $file
*
* @return TransportInterface
*/
protected function addAttachment($transport, $filePath)
{
$fileMimeType = $this->mime->getMimeType($filePath);
$fileContent = $this->fileDriver->fileGetContents($filePath);
$html = $transport->getMessage()->getBody()->generateMessage();
$decodedHtml = quoted_printable_decode($html);
$message = $transport->getMessage();
if (!$message) {
throw new \Magento\Framework\Exception\LocalizedException(__('Email message is null.'));
}
$attachment = new Part($fileContent);
$attachment->disposition = LaminasMime::DISPOSITION_ATTACHMENT;
$attachment->encoding = LaminasMime::MULTIPART_RELATED;
$attachment->filename = basename($filePath);
$attachment->type = $fileMimeType;
$htmlPart = new Part($decodedHtml);
$htmlPart->type = 'text/html';
$htmlPart->charset = 'utf-8';
$htmlPart->encoding = LaminasMime::ENCODING_QUOTEDPRINTABLE;
$mimeMessage = new MimeMessage();
$mimeMessage->setParts([$htmlPart, $attachment]);
$message->setBody($mimeMessage);
return $transport;
}
}
Below is the email template file your_template_name needs to be created under view/frontend/email/your_template_name.xml
<!-- /**
* Webkul Software Private Limited
*
* @category Webkul Software Private Limited
* @package Webkul_Email
* @author Webkul Software Private Limited
* @copyright Webkul Software Private Limited (https://webkul.com)
* @license https://store.webkul.com/license.html
*/ -->
<!--@subject {{var message_email_subject}} @-->
<!--@vars {
"var message_email_subject":"Message Email Subject"
"var message_email_content|raw":"Message Email Content"
"var recipient_name":"Recipient Name"
} @-->
{{template config_path="design/email/header_template"}}
<p>Dear {{var recipient_name}},</p>
<p>{{var message_email_content|raw}}</p>
{{template config_path="design/email/footer_template"}}
Here is the output below. Where attachment is added in the mail.

Current Product Version - 1
Supported Framework Version - Magento 2.0.x, 2.1.x, 2.2.x,2.3.x, 2.4.x

Be the first to comment.