Why are Laravel emails showing embedded images as attachments?

1k Views Asked by At

I just updated the Laravel version for my project from 7 to 9, which switches to using Symfony Mailer instead of SwiftMailer, under the hood. So in my emails, I'm using this method (Inline Attachments) which is specified in the documentation for embedding images. Still, all emails containing embedded images are interpreted by email clients as having attachments (meaning the attachment icon is visible, usually a paperclip). In contrast, previously, in version 7, this was not the case.

How would I fix this so the email client correctly shows the attachment icon only when I attach a separate document to the email?

2

There are 2 best solutions below

3
Giacomo Tüfekci On BEST ANSWER

The issue you are facing is most likely because Symfony Mailer includes inline images as attachments by default. To fix this, you need to add a Content-Disposition header to each inline image to indicate that it should be displayed inline rather than as an attachment.

https://symfony.com/doc/current/mailer.html#embedding-images

use Symfony\Component\Mime\Part\DataPart;

// ...

$imageData = file_get_contents('/path/to/image.png');

$imagePart = new DataPart($imageData, 'image/png', 'inline');
$imagePart->setFilename('image.png');
$imagePart->headers->set('Content-Disposition', 'inline');

$mail->addPart($imagePart);
0
Balaji Kandasamy On

Modifying the Content-Disposition header to inline for embedded images may fix this issue. You can do this by creating a custom Mailer class that extends the Symfony Mailer class and overrides the createAttachment method to set the correct header for inline images.

Below is a mailer class example:

namespace App\Mail;

use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\InlinePart;
use Symfony\Component\Mime\Part\PartInterface;
use Symfony\Component\Mime\Attachment\Attachment;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Message;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailer as BaseMailer;

class Mailer extends BaseMailer implements MailerContract
{
    /**
     * Create a new message instance.
     *
     * @return \Symfony\Component\Mime\Message
     */
    protected function createMessage()
    {
        $message = new Email();
        $this->addContent($message);

        return $message;
    }

    /**
     * Create a new attachment.
     *
     * @param  string|\Symfony\Component\Mime\Part\PartInterface  $file
     * @param  array  $options
     * @return \Symfony\Component\Mime\Attachment
     */
    public function createAttachment($file, array $options = [])
    {
        if ($file instanceof PartInterface) {
            return new InlinePart($file->body(), $file->getHeaders());
        }

        $attachment = Attachment::fromPath($file);

        if (isset($options['filename'])) {
            $attachment->filename($options['filename']);
        }

        if (isset($options['mime'])) {
            $attachment->contentType($options['mime']);
        }

        if (isset($options['disposition'])) {
            $attachment->disposition($options['disposition']);
        }

        return $attachment;
    }
}

Change settings in config/mail.php like below to use custom mailer class.

'mailer' => env('MAIL_MAILER', 'smtp'),
'smtp' => [
    'transport' => 'smtp',
    'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
    'port' => env('MAIL_PORT', 587),
    'encryption' => env('MAIL_ENCRYPTION', 'tls'),
    'username' => env('MAIL_USERNAME'),
    'password' => env('MAIL_PASSWORD'),
    'timeout' => null,
    'auth_mode' => null,
],
'pretend' => false,