跳到内容

MIME 组件

编辑此页

Mime 组件允许操作用于发送电子邮件的 MIME 消息,并提供与 MIME 类型相关的实用工具。

安装

1
$ composer require symfony/mime

注意

如果您在 Symfony 应用程序之外安装此组件,则必须在代码中引入 vendor/autoload.php 文件,以启用 Composer 提供的类自动加载机制。阅读这篇文章以获取更多详细信息。

简介

MIME(多用途互联网邮件扩展)是一种互联网标准,它扩展了电子邮件的原始基本格式,以支持以下功能:

  • 使用非 ASCII 字符的标头和文本内容;
  • 具有多个部分的消息体(例如,HTML 和纯文本内容);
  • 非文本附件:音频、视频、图像、PDF 等。

整个 MIME 标准复杂而庞大,但 Symfony 抽象了所有这些复杂性,提供了两种创建 MIME 消息的方法:

  • 基于 Email 类的高级 API,用于快速创建具有所有常用功能的电子邮件消息;
  • 基于 Message 类的低级 API,用于完全控制电子邮件消息的每个部分。

用法

使用 Email 类及其链式方法来撰写整个电子邮件消息

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Mime\Email;

$email = (new Email())
    ->from('fabien@symfony.com')
    ->to('foo@example.com')
    ->cc('bar@example.com')
    ->bcc('baz@example.com')
    ->replyTo('fabien@symfony.com')
    ->priority(Email::PRIORITY_HIGH)
    ->subject('Important Notification')
    ->text('Lorem ipsum...')
    ->html('<h1>Lorem ipsum</h1> <p>...</p>')
;

此组件的唯一目的是创建电子邮件消息。使用 Mailer 组件来实际发送它们。

Twig 集成

Mime 组件与 Twig 进行了出色的集成,允许您从 Twig 模板创建消息、嵌入图像、内联 CSS 等。有关如何使用这些功能的详细信息,请参阅 Mailer 文档:Twig:HTML 和 CSS

但是,如果您在没有 Symfony 框架的情况下使用 Mime 组件,则需要处理一些设置细节。

Twig 设置

要与 Twig 集成,请使用 BodyRenderer 类来渲染模板,并使用结果更新电子邮件消息内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...
use Symfony\Bridge\Twig\Mime\BodyRenderer;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;

// when using the Mime component inside a full-stack Symfony application, you
// don't need to do this Twig setup. You only have to inject the 'twig' service
$loader = new FilesystemLoader(__DIR__.'/templates');
$twig = new Environment($loader);

$renderer = new BodyRenderer($twig);
// this updates the $email object contents with the result of rendering
// the template defined earlier with the given context
$renderer->render($email);

内联 CSS 样式(以及其他扩展)

要使用 inline_css 过滤器,请先安装 Twig 扩展

1
$ composer require twig/cssinliner-extra

现在,启用扩展

1
2
3
4
5
6
// ...
use Twig\Extra\CssInliner\CssInlinerExtension;

$loader = new FilesystemLoader(__DIR__.'/templates');
$twig = new Environment($loader);
$twig->addExtension(new CssInlinerExtension());

对于启用其他扩展(例如 MarkdownExtensionInkyExtension)也应使用相同的过程。

创建原始邮件消息

这对于需要完全控制每个电子邮件部分的高级应用程序非常有用。对于具有常规电子邮件要求的应用程序,不建议使用它,因为它会增加复杂性而没有实际收益。

在继续之前,重要的是了解电子邮件消息的低级结构。考虑一条消息,其中包含文本和 HTML 格式的内容,一个嵌入在这些内容中的 PNG 图像以及一个附加到它的 PDF 文件。MIME 标准允许以不同的方式构造此消息,但以下树结构在大多数电子邮件客户端上都有效:

1
2
3
4
5
6
7
multipart/mixed
├── multipart/related
│   ├── multipart/alternative
│   │   ├── text/plain
│   │   └── text/html
│   └── image/png
└── application/pdf

这是每个 MIME 消息部分的目的:

  • multipart/alternative:当两个或多个部分是相同(或非常相似)内容的替代项时使用。首选格式必须最后添加。
  • multipart/mixed:用于在同一消息中发送不同的内容类型,例如附加文件时。
  • multipart/related:用于指示每个消息部分都是聚合整体的组件。最常见的用途是显示嵌入在消息内容中的图像。

当使用低级 Message 类创建电子邮件消息时,您必须牢记以上所有内容,以便手动定义电子邮件的不同部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
use Symfony\Component\Mime\Part\TextPart;

$headers = (new Headers())
    ->addMailboxListHeader('From', ['fabien@symfony.com'])
    ->addMailboxListHeader('To', ['foo@example.com'])
    ->addTextHeader('Subject', 'Important Notification')
;

$textContent = new TextPart('Lorem ipsum...');
$htmlContent = new TextPart('<h1>Lorem ipsum</h1> <p>...</p>', null, 'html');
$body = new AlternativePart($textContent, $htmlContent);

$email = new Message($headers, $body);

通过创建适当的电子邮件多部分,可以嵌入图像和附加文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
use Symfony\Component\Mime\Part\Multipart\RelatedPart;

// ...
$embeddedImage = new DataPart(fopen('/path/to/images/logo.png', 'r'), null, 'image/png');
$imageCid = $embeddedImage->getContentId();

$attachedFile = new DataPart(fopen('/path/to/documents/terms-of-use.pdf', 'r'), null, 'application/pdf');

$textContent = new TextPart('Lorem ipsum...');
$htmlContent = new TextPart(sprintf(
    '<img src="cid:%s"/> <h1>Lorem ipsum</h1> <p>...</p>', $imageCid
), null, 'html');
$bodyContent = new AlternativePart($textContent, $htmlContent);
$body = new RelatedPart($bodyContent, $embeddedImage);

$messageParts = new MixedPart($body, $attachedFile);

$email = new Message($headers, $messageParts);

序列化邮件消息

使用 EmailMessage 类创建的电子邮件消息可以序列化,因为它们是简单的数据对象

1
2
3
4
5
6
$email = (new Email())
    ->from('fabien@symfony.com')
    // ...
;

$serializedEmail = serialize($email);

一个常见的用例是存储序列化的电子邮件消息,将它们包含在使用 Messenger 组件发送的消息中,并在稍后发送时重新创建它们。使用 RawMessage 类从其序列化内容中重新创建电子邮件消息

1
2
3
4
5
6
7
use Symfony\Component\Mime\RawMessage;

// ...
$serializedEmail = serialize($email);

// later, recreate the original message to actually send it
$message = new RawMessage(unserialize($serializedEmail));

MIME 类型实用工具

尽管 MIME 主要用于创建电子邮件,但 MIME 标准定义的内容类型(也称为 MIME 类型和“媒体类型”)在电子邮件之外的通信协议(如 HTTP)中也很重要。这就是为什么此组件还提供用于处理 MIME 类型的实用工具。

MimeTypes 类在 MIME 类型和文件名扩展名之间进行转换

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Mime\MimeTypes;

$mimeTypes = new MimeTypes();
$exts = $mimeTypes->getExtensions('application/javascript');
// $exts = ['js', 'jsm', 'mjs']
$exts = $mimeTypes->getExtensions('image/jpeg');
// $exts = ['jpeg', 'jpg', 'jpe']

$types = $mimeTypes->getMimeTypes('js');
// $types = ['application/javascript', 'application/x-javascript', 'text/javascript']
$types = $mimeTypes->getMimeTypes('apk');
// $types = ['application/vnd.android.package-archive']

这些方法返回包含一个或多个元素的数组。元素位置指示其优先级,因此第一个返回的扩展名是首选扩展名。

猜测 MIME 类型

另一个有用的实用工具允许猜测任何给定文件的 MIME 类型

1
2
3
4
5
6
use Symfony\Component\Mime\MimeTypes;

$mimeTypes = new MimeTypes();
$mimeType = $mimeTypes->guessMimeType('/some/path/to/image.gif');
// Guessing is not based on the file name, so $mimeType will be 'image/gif'
// only if the given file is truly a GIF image

猜测 MIME 类型是一个耗时的过程,需要检查文件内容的一部分。Symfony 应用了多种猜测机制,其中一种基于 PHP fileinfo 扩展。建议安装该扩展以提高猜测性能。

添加 MIME 类型猜测器

您可以通过创建一个实现 MimeTypeGuesserInterface 的类来添加您自己的 MIME 类型猜测器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
namespace App;

use Symfony\Component\Mime\MimeTypeGuesserInterface;

class SomeMimeTypeGuesser implements MimeTypeGuesserInterface
{
    public function isGuesserSupported(): bool
    {
        // return true when the guesser is supported (might depend on the OS for instance)
        return true;
    }

    public function guessMimeType(string $path): ?string
    {
        // inspect the contents of the file stored in $path to guess its
        // type and return a valid MIME type ... or null if unknown

        return '...';
    }
}

MIME 类型猜测器必须注册为服务,并使用 mime.mime_type_guesser 标签进行标记。如果您使用的是默认的 services.yaml 配置,则由于自动配置,这已经为您完成了。

这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本