Asynchronous e-mails with Symfony Mailer
TL;DR
If you are using Symfony Mailer, Symfony Messenger and are sending e-mails with attachments asynchronously you might have come across some issues:
- An email cannot be sent, because the (temporary) attachment files do not exist anymore.
- An email failed to send, and the message ended up in a “failed” queue. After retrying to send the message, the attachments files do not exist anymore.
- An email contains binary data, which is bloating the message transport. When attaching a file to an email using
attach
(instead ofattachFromPath
), binary data is included in the messages itself.
Install fusonic/messenger-mailer-bundle to solve the issues above.
The Problem
If e-mails are important in your application then handling them asynchronously is essential. You do not want your e-mails to live in memory. If the system crashes, they will be gone!
Swiftmailer has been a popular option for sending e-mails for a long time. This has now changed since the introduction of Symfony Mailer. Swiftmailer had an option to save e-mails to the disk before sending. These files would also include attachments. Swiftmailer does not have the problem we are addressing here.
Symfony Mailer uses Symfony Messenger. Emails should get send over an asynchronous message transport. This is where problems arise if you are adding attachments to e-mails. If you attach files by path (using attachFromPath
on the Email
class), the attachments will not live in the same place as the email itself. To make sure that e-mails will contain the files, you will have to make sure that the files exist!
If you attach files (using attach
on the Email
class) directly, it will work! However, this will result in your message queue containing the complete messages with the binary data. This is something you might want to avoid. Depending on the transport, different problems could come up, for example:
- The queue might get too large and will impact the performance.
- Messages can become too big if you attach large files.
Concluding from the above problems: you want to keep binary data outside your message transport.
Solution
Our bundle abstracts the handing of managing attachments away.
A new class AttachmentEmail
is introduced, which extends the built in Email
class from Symfony Mailer. This class contains a new id
property and has two new methods:
attachPersistedFromPath
: Similar toattachFromPath
, but files are copied and persisted until the actual e-mail has been sent.attachPersisted
: Similar toattach
, but the content is persisted to a file until the actual e-mail has been sent.
By using the methods above you are making sure that attachments will be handled by the configured EmailAttachmentHandlerInterface
. By default, a filesystem based implementation will be used. This implementation will store the files in a temporary directory, located in var/email-attachments/{email-id}
. The handler will automatically create/copy files to the temporary directory and remove them after the e-mail has been sent. This works by utilizing the SendMessageToTransportsEvent
and WorkerMessageHandledEvent
Symfony Messenger events.
Getting started
Head over to fusonic/messenger-mailer-bundle for installation, requirements and usage instructions.