如何从MIME内容创建图像?

时间:2019-01-28 16:22:22

标签: c# mime mime-message

我正在尝试在.NET Core 2.2框架顶部使用C#编写一个小型控制台应用程序。

控制台应用程序将向外部API发出HTTP请求以获取多个图像。我能够向服务器发出请求并获得响应。但是,服务器使用MIMI消息进行多部分响应。

我能够解析请求并为每条消息获取MIME正文。但是,我无法弄清楚如何根据正文内容创建文件。

以下是原始MIMI消息的开头示例 enter image description here

我尝试将主体作为字符串写入文件,但是没有用

string body = GetMimeBody(message);
File.WriteAllText("image_from_string" + MimeTypeMap.GetExtension(contentType), bytes);

我也试图将字符串转换为byte[],但仍然无法正常工作

byte[] bytes = Encoding.ASCII.GetBytes(body);
File.WriteAllBytes("image_from_ascii_bytes" + MimeTypeMap.GetExtension(contentType), bytes);

byte[] bytes = Encoding.Default.GetBytes(body);
File.WriteAllBytes("image_from_default_bytes" + MimeTypeMap.GetExtension(contentType), bytes);


byte[] bytes = Encoding.UTF8.GetBytes(body);
File.WriteAllBytes("image_from_utf8_bytes" + MimeTypeMap.GetExtension(contentType), bytes);

“不起作用”是指图像无法正确打开。图片查看器显示“图像似乎已损坏或损坏。”

如何从邮件中正确地制作出好的图像?

已更新

这是代码以及解析部分

var responseContentType = response.Content.Headers.GetValues("Content-Type").FirstOrDefault();
string splitter = string.Format("--{0}", GetBoundary(responseContentType));
string content = await response.Content.ReadAsStringAsync();
var messages = content.Split(splitter, StringSplitOptions.RemoveEmptyEntries);

foreach (var message in messages)
{
    var mimiParts = message.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
    if (mimiParts.Length == 0)
    {
        continue;
    }

    string contentId = Str.GetValue("Content-ID", mimiParts, ':');
    string objectId = Str.GetValue("Object-ID", mimiParts, ':');
    string contentType = Str.GetValue("Content-Type", mimiParts, ':');

    if (string.IsNullOrWhiteSpace(contentId) || string.IsNullOrWhiteSpace(objectId) || string.IsNullOrWhiteSpace(contentType))
    {
        continue;
    }

    string body = mimiParts[mimiParts.Length - 1];

    var filename = string.Format("{0}_{1}{2}", contentId, objectId, MimeTypeMap.GetExtension(contentType));

    var decoded = System.Net.WebUtility.HtmlDecode(data);
    File.WriteAllText("image_from_html_decoded_bytes" + filename, decoded);
}

这是解析消息的方法

public class Str
{
    public static string GetValue(string startWith, string[] lines, char splitter = '=')
    {
        foreach (var line in lines)
        {
            var value = line.Trim();

            if (!value.StartsWith(startWith, StringComparison.CurrentCultureIgnoreCase) || !line.Contains(splitter))
            {
                continue;
            }

            return value.Split(splitter)[1].Trim();
        }

        return string.Empty;
    }
}

以下是屏幕截图,显示了mimiParts变量的内容 enter image description here

更新2

根据下面的反馈,我尝试使用MimeKit软件包,而不是尝试自己解析响应。以下是我尝试消耗响应的方式。但是,我仍然收到与上述相同的错误。写入图像文件时,出现图像损坏错误。

var responseContentType = response.Content.Headers.GetValues("Content-Type").FirstOrDefault();

if (!ContentType.TryParse(responseContentType, out ContentType documentContentType))
{
    return;
}

var stream = await response.Content.ReadAsStreamAsync();

MimeEntity entity = MimeEntity.Load(documentContentType, stream);
Multipart messages = entity as Multipart;

if (messages == null)
{
    throw new Exception("Unable to cast entity to Multipart");
}

foreach (MimeEntity message in messages)
{
    string contentId = message.Headers["Content-ID"];
    string objectId = message.Headers["Object-ID"];
    string contentType = message.Headers["Content-Type"];

    if (string.IsNullOrWhiteSpace(contentId) || string.IsNullOrWhiteSpace(objectId) || string.IsNullOrWhiteSpace(contentType))
    {
        continue;
    }

    var filename = string.Format("{0}_{1}{2}", contentId, objectId, MimeTypeMap.GetExtension(contentType));

    message.WriteTo(filename);
}

2 个答案:

答案 0 :(得分:2)

MIME编码很困难,并且将服务器发送的字节当作字符串对待已经是一个错误。在换行符处拆分将产生更多问题。二进制表示0x00到0xff之间的每个值都是有效的。但是Unicode和ASCII的有效字节范围不同,尤其是转换它们是有问题的。 .NET内部字符串类将每个字符解释为两个字节。 HttpContent.ReadAsStringAsync运行时,它将尝试将从服务器接收到的每个单字节解释为两个字节的Unicode字符。我很确定您将无法从该数据丢失中恢复过来。

  • 使用HxD之类的十六进制编辑器将图像的良好副本与您从应用程序中写出的图像进行比较,以查找差异。至少如果您想坚持自己的代码。但是我确定您仍然需要从字符串操作切换到Stream操作。
  • 使用已制成的MIME解析库。一个示例是MimeKit。这将大大减少您的开发时间。

仅供参考,这是JPG的前10个字节的外观:

FF D8 FF E0 00 10 4A 46 49 46      ÿØÿà..JFIF

答案 1 :(得分:2)

MimeEntity.WriteTo (file)将不幸地包含MIME标头,这是导致损坏的错误的原因。

您需要做的是将MimeEntity转换为MimePart,然后使用MimePart.Content.DecodeTo (stream)保存解码的内容:

var responseContentType = response.Content.Headers.GetValues("Content-Type").FirstOrDefault();

if (!ContentType.TryParse(responseContentType, out ContentType documentContentType))
{
    return;
}

var stream = await response.Content.ReadAsStreamAsync();

MimeEntity entity = MimeEntity.Load(documentContentType, stream);
Multipart multipart = entity as Multipart;

if (multipart == null)
{
    throw new Exception("Unable to cast entity to Multipart");
}

foreach (MimePart part in multipart.OfType<MimePart> ())
{
    string contentType = part.ContentType.MimeType;
    string contentId = part.ContentId;
    string objectId = part.Headers["Object-ID"];

    if (string.IsNullOrWhiteSpace(contentId) || string.IsNullOrWhiteSpace(objectId) || string.IsNullOrWhiteSpace(contentType))
    {
        continue;
    }

    var filename = string.Format("{0}_{1}{2}", contentId, objectId, MimeTypeMap.GetExtension(contentType));

    using (var output = File.Create (filename))
        part.Content.DecodeTo (output);
}