得快点。优化IMAP电子邮件内容请求

大家好!在上一篇文章中,我讨论了如何快速同步本地缓存中框的内容。在这里,我想谈谈请求字母内容的功能以及如何最好地请求内容,而不用担心高流量消耗。

图片

让我们快速记住上一篇文章中学到的内容:

  • IMAP是有状态协议
  • 要查看收件箱中的内容,必须首先使用SELECT命令将其选中
  • 要快速同步我们所在的盒子,可以使用NOOP命令
  • 为了不对本地存储中的邮件进行排序以更新我们已经离开的邮箱,可以使用CONDSTORE和QRESYNC,前提是您的服务器支持协议扩展数据

足够!


让我提醒您,命令要求输入正文:

1 FETCH number (BODY[])

这将创建一个请求,以获取信函及其所有附件的全文。只需看看在Lorem Ipsum的42段中收到一条消息所需的时间,并带有2兆字节的图片即可。

首先,询问服务器上消息的大小。这是通过以下命令完成的:

1 FETCH 18871 (RFC822.SIZE)

RFC822.SIZE返回消息的大小(以字节为单位):

* 18871 FETCH (RFC822.SIZE 3937793)

也就是说,我们的消息占用了将近4兆字节。

但是,现在,我们将使用该请求作为正文的全部内容,并查看时间:

1 OK Fetch completed (0.007 + 3.265 secs).

3.3秒!这只是带有附件的一封邮件,请想象它们将成为整个邮件箱。然后,将需要一分钟以上的时间来下载至少二十个第一个。

您必须承认,到2020年无法同步邮件的客户的业务是糟糕的。但是该怎么办?

给我一口


如果您在第6.5.4节(该节描述了FETCH命令的可能参数)中沙漏RFC3501,您会注意到一个有趣的请求:

BODY[<section>]<<partial>>

  • 部分-要获得信件的哪一部分
  • 部分-此部分的大小

如何部分构建?而且非常简单。首先,从该点开始写入需要开始读取的字节,然后一般应读取多少个字节:

BODY[<section>]<<0.1024>>

在这里,我们要求字母的一部分从零字节到1024。

好吧,什么是节?首先,我将在FETCH查询中讨论诸如BODYSTRUCTURE这样有用的参数:

1 FETCH 18871 (BODYSTRUCTURE)

正如您可能从签名中了解的那样,此参数以MIME-IMB中描述的形式返回字母的结构

* 18871 FETCH (BODYSTRUCTURE ((("text" "plain" ("charset" "utf-8") NIL NIL "quoted-printable" 25604 337 NIL NIL NIL NIL)("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 29593 390 NIL NIL NIL NIL) "alternative" ("boundary" "--=_Part_763_774309787.1586268692") NIL NIL NIL)("image" "jpeg" ("name" "IMG_20200217_000236.jpg") NIL NIL "base64" 3880726 NIL ("attachment" ("filename" "IMG_20200217_000236.jpg")) NIL NIL) "mixed" ("boundary" "--=_Part_210_297656922.1586268692") NIL NIL NIL))


看看这个结构,您的头在旋转吗?不要害怕,现在我们将解决这个问题。比较打开和关闭支架。

(
BODYSTRUCTURE 
(
[1] (
[1.1] ("text" "plain" ("charset" "utf-8") NIL NIL "quoted-printable" 25604 337 NIL NIL NIL NIL)
[1.2] ("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 29593 390 NIL NIL NIL NIL) "alternative" ("boundary" "--=_Part_763_774309787.1586268692") NIL NIL NIL
)
[2] ("image" "jpeg" ("name" "IMG_20200217_000236.jpg") NIL NIL "base64" 3880726 NIL ("attachment" ("filename" "IMG_20200217_000236.jpg")) NIL NIL) "mixed" ("boundary" "--=_Part_210_297656922.1586268692") NIL NIL NIL
)
)


您可能会注意到,我将数字放在了一些方括号附近。这是一节。
如何计算?必须跳过第一个括号,因为它仅包含请求的答案,因此,每个开头的括号必须根据规则编号,因为文档中的标题编号为:

  • 我们在考虑到上一节的情况下为每个开括号编号
  • 如果该部分是嵌套的,则当前的一点到该点将被添加到先前的数字中
  • 如果该部分未嵌套,则将其数量增加一


例如,在这种情况下,在以“替代”结尾的第一部分(即,这是多部分/替代字母的部分,我们可以自由选择要为用户显示的部分)中有两个部分用点编号。我遇到了一个可能存在三级嵌套的服务器(即[1.1.1],[1.1.2]等)。
让我们分析[1.1]部分,看看MIME-IMB文档中所有这些东西的结构以此判断,Content-Type头是第一个。这包括:

  • MIME类型,这里是文本/纯文本
  • 编码(字符集= utf8)


以下是写为NIL的两个参数。坦白说,我不了解它是什么,但是到目前为止我还不需要它,所以我会想念它的。对此我很抱歉,
接下来是Content-Transfer-Encoding标头,它包含在其中,它描述了编码机制,在这里它被引用为可打印的。 
以下两个数字(如果可能)以字节为单位描述部件的大小和行数。在他们的帮助下,我们可以计算出显示一定数量行所需的字节数。
以下几行不在本部分中:

  • Content-Id,用于字母内联
  • Content-Description,描述这部分内容的一行


对于其他两个参数,我找不到确切的答案,但是其中一个参数可能包含MD5部分,有时可能很有用。
对于部分[2],所有内容都相同,除了它是图像,带有名称的附件和base64编码。如果仍然不能完全清楚这里发生了什么,那么在此站点上,它可以完美地列出确切的部分计算方法。

它有什么作用?事实是,在显示字母的阶段,我们只可以请求内容的顶部,而在用户自己输入消息并单击“下载”按钮之前,不加载附件。用于显示附件的所有信息都通过BODYSCTRUCTURE提供给我们,因此无需加载附件本身即可显示名称,格式和大小。 

让我们继续练习。我们会要求提供一千字节的不带附件的邮件内容,只是为了知道它们向我们发送了什么。

1 fetch 18871 (body[1.1]<0.1024>)
* 18871 FETCH (BODY[1.1]<0> {1024}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consecte=
tur enim in nisi venenatis, id varius tellus viverra. Praesent et enim te=
llus. Nunc vestibulum diam tortor, id posuere turpis tempor luctus. Vivam=
us molestie non nunc nec placerat. Cras finibus ut erat et tristique. Cur=
abitur vitae commodo risus. Etiam sed scelerisque erat. Quisque cursus bl=
andit finibus. Nullam ac lectus accumsan, molestie quam non, mollis urna.=
 Nulla at arcu in libero condimentum mollis ut non velit. Vestibulum sed =
risus et magna congue iaculis. Vestibulum nec interdum elit, ut commodo m=
auris. Nulla ipsum leo, vestibulum nec ligula non, elementum ullamcorper =
risus. Nunc et malesuada sem, id venenatis massa. Integer dolor ante, max=
imus in eleifend nec, ultricies ut risus. Mauris posuere eget tortor at p=
orttitor.=0AIn porta elementum ornare. Suspendisse aliquam, tortor sed al=
iquam bibendum, nulla ante rhoncus elit, placerat accumsan augue nibh non=
 est. Duis finibus vel tortor finibu)
1 OK Fetch completed (0.073 + 0.000 + 0.072 secs).


大约100毫秒,我们已经看到了信件的某些内容!鉴于以前下载一个字母的内容花了我们近4秒钟,所以这只是一个很好的结果。然后,您可以简单地将字母的整个内容加载到后台流中,从外面看,字母似乎是立即加载的。所需要做的只是查看字母的结构并仅下载快速显示所需的内容。 
只有一刻。该请求将使服务器上的消息显示为已读。但是您可以通过仅在正文请求中添加PEEK来解决此问题

1 fetch 18871 (BODY.PEEK[1.1]<0.1024>)
* 18871 FETCH (BODY[1.1]<0> {1024}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consecte=
tur enim in nisi venenatis, id varius tellus viverra. Praesent et enim te=
llus. Nunc vestibulum diam tortor, id posuere turpis tempor luctus. Vivam=
us molestie non nunc nec placerat. Cras finibus ut erat et tristique. Cur=
abitur vitae commodo risus. Etiam sed scelerisque erat. Quisque cursus bl=
andit finibus. Nullam ac lectus accumsan, molestie quam non, mollis urna.=
 Nulla at arcu in libero condimentum mollis ut non velit. Vestibulum sed =
risus et magna congue iaculis. Vestibulum nec interdum elit, ut commodo m=
auris. Nulla ipsum leo, vestibulum nec ligula non, elementum ullamcorper =
risus. Nunc et malesuada sem, id venenatis massa. Integer dolor ante, max=
imus in eleifend nec, ultricies ut risus. Mauris posuere eget tortor at p=
orttitor.=0AIn porta elementum ornare. Suspendisse aliquam, tortor sed al=
iquam bibendum, nulla ante rhoncus elit, placerat accumsan augue nibh non=
 est. Duis finibus vel tortor finibu)
1 OK Fetch completed (0.001 + 0.000 secs).


瞧!该信仍未阅读,我们收到了一些内容。
如果在服务器上实现了PREVIEW请求功能,一切将变得更加容易。 

1 fetch 18871 (PREVIEW)
* 18871 FETCH (PREVIEW (FUZZY "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur enim in nisi venenatis, id varius tellus viverra. Praesent et enim tellus. Nunc vestibulum diam tortor, id posuere turpis t"))
1 OK Fetch completed (0.001 + 0.000 secs).


在这里,我们根本不需要花费时间来查询结构,因此可以立即获得消息预览。但是请不要忘记,结构请求对于标识附件很有用,这样它们就不会加载到空闲状态中。

等待


如果用户现在想接收新的信件,几乎所有的邮件客户端都会实现“刷新”按钮。但是,这对于我们这个时代来说并不酷,因为设备和浏览器中都有通知。IMAP对此有何评论?他说闲话此操作将保持与文件夹的连接,并通知您对文件夹的更改。请注意,不是邮箱,而是文件夹。为此,您需要服务器实现IDLE功能。 

首先,选择服务器将为其发送警报的文件夹,然后启用IDLE

1 SELECT Inbox
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft $Forwarded $MDNSent)
* OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft $Forwarded $MDNSent \*)] Flags permitted.
* 18872 EXISTS
* 0 RECENT
* OK [UNSEEN 18685] First unseen.
* OK [UIDVALIDITY 1532079879] UIDs valid
* OK [UIDNEXT 20155] Predicted next UID
* OK [HIGHESTMODSEQ 26338] Highest
1 OK [READ-WRITE] Select completed (0.002 + 0.000 + 0.001 secs).
1 IDLE
+ idling


答案“ +空闲”通知有关文件夹中包含空闲的信息。如果收到新来信怎么办?

* 18873 EXISTS
* 1 RECENT


我给自己寄了同一封信,艾德勒通知我,我应该要求写18873号信,文件夹中有18873个信,并且一封信刚到。
接下来,我将在另一个连接中请求此信件,我们对带有答案EXISTS的信件感兴趣。

1 fetch 18873 (BODY.PEEK[1.1]<0.1024>)
* 18873 FETCH (BODY[1.1]<0> {1024}
---- Original Message ---- Tue, Apr 7, 2020, 17:11=0ASubject=
: Lorem Ipsum=0A  Lorem ipsum dolor sit amet, consectetur adipiscing elit=
. Vivamus consectetur enim in nisi venenatis, id varius tellus viverra. P=
raesent et enim tellus. Nunc vestibulum diam tortor, id posuere turpis te=
mpor luctus. Vivamus molestie non nunc nec placerat. Cras finibus ut erat=


了解这一点非常重要。IDLE需要单独的连接,因此您无法在同一会话中接收更改和请求消息,
IDLE还能做什么?他能够通知已删除的字母和标志已更改的字母。为了举例说明,让我们看一下字母,从而在其上放置“ / seen”标志并删除该字母。

* 18873 FETCH (FLAGS (\Seen \Recent))
* OK Still here
* OK Still here
* 18873 EXPUNGE
* 18871 EXPUNGE
* 0 RECENT


我删除了对话(18873,18871),然后看了另一封信(FETCH响应)。这封信为什么变成18871?因为如果某些更改,IMAP会重新计算字母数字。自从它成为第一名以来,其数量也发生了变化。 
使用IDLE,我们可以快速同步盒子的状态,但是它需要单独的连接是不愉快的。会更好吗?这就是为什么我在这里。

大吼你好


如果我告诉您,有一项功能可以让您从同一连接中的服务器接收通知,甚至为邮箱(甚至没有一个邮箱)进行了特殊配置。这听起来像童话故事,但请不要发疯,这是一种真正的能力NOTIFY他知道很多,例如:

  • 配置我们希望从中收到通知的特定文件夹
  • 收听文件夹状态更改(阅读字母,新字母)
  • 设置通知格式,即我们想要在更改文件夹时看到的内容
  • 听文件夹名称更改
  • 听文件夹元数据更改


让我们看一个如何监听文件夹状态变化的例子

1 notify set (inboxes (MessageNew FlagChange MessageExpunge))
1 OK NOTIFY completed (0.001 + 0.000 secs).


现在服务器将向我们发送有关文件夹状态的通知,例如,我将几条消息添加到不同的文件夹中

* STATUS INBOX/Ozon (MESSAGES 312 UIDNEXT 321 UNSEEN 48)
* STATUS "INBOX/Company News" (MESSAGES 178 UIDNEXT 179 UNSEEN 1)
* STATUS "INBOX/Company News" (MESSAGES 177 UIDNEXT 179 UNSEEN 0)


我将分析命令:
首先是NOTIFY SET命令。接下来,在方括号中,我们选择要收听的文件夹:

  • 收件箱-对于您可以选择的所有文件夹
  • 个人-用户命名空间中的文件夹
  • 已订阅-用户已订阅的文件夹
  • 子树-要指定的文件夹的子树
  • 邮箱-在这里您可以列出要收听的文件夹。
  • 选定-仅对选定的文件夹发出警报


以及负责警报过滤器的参数:

  • MessageNew-如果有新消息到达
  • FlagChange-如果标志已更改
  • MessageExpunge-如果邮件已删除或移动


但是使用这样的命令,我们无法接收新消息,更改消息或删除消息的参数。为此,选择Selected参数并指定确切返回的内容。我们可以添加另一个警报,而不会删除前一个警报。

1 notify set status (selected (MessageNew (uid preview) MessageExpunge))


在MessageNew中,我们指定通知应返回的参数。我将选择“收件箱”,然后再次将自己扔给我自己。

* 18868 FETCH (UID 20157 PREVIEW (FUZZY "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur enim in nisi venenatis, id varius tellus viverra. Praesent et enim tellus. Nunc vestibulum diam tortor, id posuere turpis t"))


你喜欢吗?对于空闲,我们需要保持两个连接,其中之一还请求空闲返回给我们的消息。他们立即将所有东西带到了银色的盘子上。 
这样我们就可以听文件夹名称的更改

1 notify set (inboxes (MailboxName))


重命名一些文件夹并查看结果。

* LIST () "/" 1111 ("OLDNAME" (aaaa))


现在我们知道有一个“ aaaa”文件夹,并变成了“ 1111”,
现在您可以收听标志的更改和消息的删除。为此,请使用FlagChange参数

1 notify set (selected (MessageNew (uid) FlagChange MessageExpunge))


当您更改消息标记并删除时,我们得到

* 18865 EXPUNGE
* 18864 FETCH (FLAGS ())
* 18864 FETCH (FLAGS (\Answered))


下一步是什么


所有这些功能都可以帮助邮件客户端为用户尽快方便地工作。IDLE和NOTIFY通知用户文件夹中的更改,请求部分信件以加快其加载速度。 
在最后一篇文章中,我想讨论一下IMAP中的搜索机制,以及如何加速搜索机制并减少网络负载。感谢您阅读到最后。 

All Articles