Как создать изображение из контента MIME? - PullRequest
0 голосов
/ 28 января 2019

Я пытаюсь написать небольшое консольное приложение, используя C# поверх платформы .NET Core 2.2.

Консольное приложение выполнит HTTP-запрос к внешнему API для получения нескольких изображений.Я могу сделать запрос на сервер и получить ответ.Однако сервер отвечает многокомпонентным ответом, используя сообщения 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 голосов
/ 29 января 2019

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);
}
0 голосов
/ 28 января 2019

MIME-кодирование сложно, и обработка байтов, отправляемых сервером как строки, уже является ошибкой.Разделение на новые строки создаст еще больше проблем.Двоичный означает, что каждое значение между 0x00 и 0xff является действительным.Но Unicode и ASCII имеют разные диапазоны допустимых байтов, и особенно их преобразование проблематично.Внутренний строковый класс .NET интерпретирует каждый символ как два байта.В момент запуска HttpContent.ReadAsStringAsync он пытается интерпретировать каждый отдельный байт, полученный от сервера, как двухбайтовый символ Unicode.Я почти уверен, что вы не сможете оправиться от этой потери данных.

  • Используйте шестнадцатеричный редактор, такой как HxD, чтобы сравнить хорошую копию изображения с той, с которой вы пишетеваше приложение и ищите различия.По крайней мере, если вы хотите придерживаться своего собственного кода.Но я уверен, что вам все равно придется переключаться с манипуляции со строками на потоковые операции.
  • Используйте уже созданную библиотеку анализа MIME.Одним из примеров является MimeKit .Это значительно сократит ваше время разработки.

В качестве примера вот как должны выглядеть первые 10 байтов JPG:

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