构造多部分 MIME 消息而不将其保存在内存中

Posted

技术标签:

【中文标题】构造多部分 MIME 消息而不将其保存在内存中【英文标题】:Construct a multipart MIME message without holding it in memory 【发布时间】:2021-03-10 03:45:52 【问题描述】:

我想从一些流中可用的部分数据(例如打开的文件句柄)中创建一个多部分 MIME 消息,并且我希望通过流发送生成的多部分 MIME 消息(例如作为一部分的网络端口HTTP 连接)。

但是,这些部分的大小为数 GB,因此我不想将它们保存在内存中,并且我不希望多部分 MIME 消息需要完全保存在内存中。

那么,如何创建多部分 MIME 消息而不将其保存在内存中,即在部分中流式传输并让它“即时”流出构造的消息?

我想使用第 3 方 MIME 库,但不知道是否支持这样的流式传输,并且我不愿意编写自己的。

有什么推荐的做法吗?

【问题讨论】:

唯一的做法是覆盖发送方法并在参数列表中包含文件名。 不确定我是否做错了什么...我真的很想知道哪个 MIME 库具有此流式传输功能,我将其发布为一个问题,但它已作为征求建议的请求而关闭,所以我根据我的基本要求重新表述了这个问题,但这已经被否决了。我认为问是否有解决我的问题的现有解决方案是一个合理的问题,但我的措辞是否有误? 这里有两个很好的链接。第一个是电子邮件 (social.msdn.microsoft.com/Forums/en-US/…) 第 3 段说你需要两个 '\r\n" 在 mime 附件之前。第二个是 Wiki (en.wikipedia.org/wiki/MIME)。当你在 Net 中使用 html 库发帖时,你附上了在调用不是你想要的方法之前的整个 mime。是创建你自己的继承 HttpWebRequest 的类,这样你就可以覆盖标准请求并添加你自己的方法来使用流发布。 @jdweng 感谢您的建议和提示!我的问题的核心实际上是“任何 mime 库都已经这样做了”,看起来答案是我需要推出自己的系统。 \r\n 是一个很好的提示。我实际上并没有通过 HTTP 发送它,所以可以处理它的那一面。 唯一的库我知道你将输出的 xml 作为字符串而不是流传递。 【参考方案1】:

MimeKit 支持从磁盘而不是内存中流式传输 MimeParts 的内容,但我不确定是否有任何 HTTP 库会为您提供原始 Socket 或 NetworkStream(或 SslStream)来写入消息。

假设 HTTP 库不是限制因素,您可以像这样在 MimeKit 中创建 MimePart:

var part = new MimePart ("application", "octet-stream") 
    Content = new MimeContent (File.OpenRead ("large-file.dat")),
    ContentTransferEncoding = ContentENcoding.Base64,
    FileName = "large-file.dat"
;

你可以像这样创建一个多部分:

var multipart = new Multipart ("mixed");
multipart.Add (part);

您可以像这样将 multipart 设置为 MimeMessage 的主体(尽管我不确定您是否真的需要 MimeMessage 容器来完成您打算做的事情):

message.Body = multipart;

您可以像这样将消息(或多部分)流式传输到网络流(或任何类型的流):

message.WriteTo (stream);

如果您需要确保即使在 Unix 系统上也将消息转换为使用 CRLF,您可以这样做:

var options = FormatOptions.Default.Clone ();
options.NewLineFormat = NewLineFormat.Dos;

message.WriteTo (options, stream);

这将通过过滤器推送消息数据,该过滤器将在写入输出流时转换行尾(如果需要转换)。

由于消息数据被分成大约 4k 块,它应该符合您的要求。

如果您还有其他问题,请在 GitHub 上找到我的电子邮件地址(相同的别名,我是 MimeKit/MailKit 的作者,所以我应该很容易找到)并给我发电子邮件。我很乐意回答您有关如何执行此操作的任何问题。

事实上,MimeKit 旨在完全按照您的意愿行事并确保一切正常,我编写了 MailKit 的原始 SmtpClient。然后我在周末写了 Pop3Client 来测试我可以解析直接从套接字读取的消息。

是的,它可以做你需要它做的事情。

【讨论】:

非常感谢,正是我想要的 :-)【参考方案2】:

经过进一步研究(受上面MimeKit 示例的启发),.Net HTTP 类以与MimeKit 类似的方式支持我正在寻找的内容。

创建一个MultipartContent 并使用StreamContent 添加部分,然后使用MultipartContent.ReadAsStreamAsync 获取多部分消息的流,直到多部分流需要它时才会读取部分流。这意味着(从监控进程大小)原始部分永远不会完全在内存中,多部分消息也不会。

【讨论】:

以上是关于构造多部分 MIME 消息而不将其保存在内存中的主要内容,如果未能解决你的问题,请参考以下文章

如何直接保存到持久存储,而不将数据保存到内存中

Android:旋转图像而不将其加载到内存中

如何使用 Java 裁剪图像而不将其加载到内存中

设置自定义异常的消息而不将其传递给基本构造函数

如何逐行读取大型文本文件,而不将其加载到内存中?

如何从 PHP 扩展返回数组,而不将其复制到内存中?