FETCH 命令是什么?
FETCH 命令允许客户端从指定的邮箱(通常是收件箱)中检索一封或多封邮件的属性,与 POP3 协议不同,IMAP 允许客户端只获取邮件的元数据(如发件人、主题、大小等),而不必立即下载整个邮件正文,这对于节省带宽和提高效率至关重要。

基本语法
FETCH 命令的基本语法如下:
FETCH <set> <fetch-attribute> [<fetch-attribute> ...]
<set>: 指定要获取邮件的集合,这可以是单个序列号、一个范围(如1:5)或一个 UID 范围(如UID 1:5),强烈推荐使用 UID,因为它在邮件删除或移动后不会改变。<fetch-attribute>: 指定要获取的邮件属性,可以是单个属性,也可以是多个属性的组合。ENVELOPE、BODY、RFC822等。
关键概念:序列号 vs. UID
在深入 FETCH 命令之前,必须理解 IMAP 中的两个关键标识符:
- 序列号: 邮件在当前邮箱中的“位置”编号。
1是第一封邮件,2是第二封。如果邮件被删除,后续邮件的序列号会自动向前递补,序列号是不稳定的。 - UID (Unique Identifier): 为每封邮件分配的一个永久、唯一的数字,只要邮件不被删除,它的 UID 就不会改变,即使它在邮箱中被移动位置。强烈建议在所有客户端操作中使用 UID,而不是序列号。
使用 UID 的命令格式通常是在 FETCH 之前加上 UID 关键字:
FETCH <uid-set> <fetch-attribute>
UID FETCH 1001 BODY.PEEK[HEADER]
常用的 <fetch-attribute> 参数
FETCH 命令的强大之处在于其灵活的属性参数,以下是一些最常用的属性:

1. 获取邮件元数据
FLAGS: 获取或设置邮件的标志(如\Seen,\Flagged,\Answered,\Draft)。FETCH 1:5 FLAGS
ENVELOPE: 获取邮件的信封信息,包括发件人、收件人、抄送、密送、主题和日期,这是获取邮件基本信息最高效的方式。FETCH 1:5 ENVELOPE
INTERNALDATE: 获取邮件到达服务器的时间。FETCH 1 INTERNALDATE
RFC822.SIZE: 获取邮件的总大小(以字节为单位)。FETCH 1:5 RFC822.SIZE
2. 获取邮件正文
-
BODY: 获取邮件的结构化内容,这是最灵活也最常用的获取正文的方式。BODY[]: 获取整个邮件(包括头部和正文)。FETCH 1 BODY[]
BODY[HEADER]: 只获取邮件头部。FETCH 1 BODY[HEADER]
BODY[TEXT]: 只获取邮件的纯文本正文。FETCH 1 BODY[TEXT]
BODY[HTML]: 只获取邮件的 HTML 格式正文。FETCH 1 BODY[HTML]
BODY[HEADER.FIELDS (subject from date)]: 只获取指定的头部字段。FETCH 1 BODY[HEADER.FIELDS (subject from date)]
BODY[1]: 获取邮件的第一个 multipart 部分(通常是文本正文)。BODY[1.1]: 获取第一个 multipart 下的第一个子部分(HTML 正文)。
-
BODY.PEEK: 与BODY类似,但不会将邮件标记为已读(\Seen),这对于预览邮件非常有用。FETCH 1 BODY.PEEK[HEADER]
-
RFC822: 获取整个邮件的原始内容,等同于BODY[]。FETCH 1 RFC822
-
RFC822.HEADER: 获取邮件的原始头部,等同于BODY[HEADER]。
(图片来源网络,侵删)FETCH 1 RFC822.HEADER
-
RFC822.TEXT: 获取邮件的原始正文(不包括附件),等同于BODY[TEXT]。FETCH 1 RFC822.TEXT
3. 高级用法
BODYSTRUCTURE: 获取邮件的详细结构信息,包括各部分的 MIME 类型、编码、大小等,这对于解析复杂邮件(带附件、多部分)非常有用。FETCH 1 BODYSTRUCTURE
UID: 获取邮件的 UID,这在不知道 UID 但想获取它时很有用。FETCH 1 UID
实际示例
假设我们有一个邮箱,里面有 5 封邮件,我们想执行一些操作。
示例 1:获取所有邮件的主题和发件人
// 使用 UID 获取更稳定的结果 UID FETCH 1:* (ENVELOPE)
服务器可能会返回类似这样的信息:
* 1 FETCH (ENVELOPE ("Wed, 1 Jan 2025 10:00:00 +0000" "Welcome to our service" (("John Doe" NIL "john" "example.com")) (("Jane Smith" NIL "jane" "example.com")) NIL NIL NIL "ID123456"))
* 2 FETCH (ENVELOPE ("Tue, 2 Jan 2025 15:30:00 +0000" "Meeting Reminder" (("Alice" NIL "alice" "company.com")) (("Bob" NIL "bob" "company.com")) NIL NIL NIL "ID789012"))
...
示例 2:预览第一封邮件的头部(不标记为已读)
UID FETCH 1 BODY.PEEK[HEADER]
示例 3:获取特定 UID 邮件的纯文本正文
UID FETCH 1001 BODY[TEXT]
示例 4:获取第三封邮件的完整结构
FETCH 3 BODYSTRUCTURE
示例 5:获取所有邮件的 UID、大小和已读状态
UID FETCH 1:* (UID RFC822.SIZE FLAGS)
最佳实践
- 优先使用 UID: 始终使用
UID FETCH而不是FETCH <sequence number>,以避免邮件删除或移动后操作失效。 - 按需获取数据: 不要轻易使用
RFC822或BODY[]下载整个邮件,特别是对于大附件,先使用ENVELOPE和BODY[HEADER]来查看邮件概况,然后根据需要选择下载BODY[TEXT]或BODY[HTML]。 - 使用
BODY.PEEK进行预览: 当用户只想查看邮件列表或预览邮件内容而不想改变其“已读”状态时,使用BODY.PEEK。 - 缓存结果: 对于不经常变化的邮件信息(如
ENVELOPE),可以在客户端进行缓存,以减少与服务器的交互。
在代码中使用(Python 示例)
使用 imaplib 库时,FETCH 命令是通过 fetch() 方法执行的。
import imaplib
import email
# 连接到 IMAP 服务器
mail = imaplib.IMAP4_SSL('imap.example.com')
mail.login('your_email@example.com', 'your_password')
# 选择收件箱
mail.select('inbox')
# 搜索所有邮件的 UID
status, messages = mail.uid('search', None, "ALL")
if status == 'OK':
# 获取最新的 5 封邮件的 UID
latest_email_uids = messages[0].split()[-5:]
for uid in latest_email_uids:
# 获取邮件的 ENVELOPE 信息(主题、发件人等)
res, data = mail.uid('fetch', uid, '(ENVELOPE)')
if res == 'OK':
print(f"UID: {uid.decode('utf-8')}")
# 解析 ENVELOPE 数据比较复杂,通常需要专门的库
# ...
# 获取邮件的纯文本正文
res, data = mail.uid('fetch', uid, '(BODY[TEXT])')
if res == 'OK':
# data[0][1] 包含邮件正文
raw_body = data[0][1]
print(f"Body preview: {raw_body[:100].decode('utf-8')}...")
mail.logout()
FETCH 命令是 IMAP 协议的基石,它提供了精细、高效地访问邮件数据的能力,理解如何使用 UID 以及不同的 fetch-attribute(如 ENVELOPE, BODY, BODY.PEEK)是构建一个功能完善、性能良好的 IMAP 客户端的关键。
