2016-08-01 9 views
11

У меня есть приложение, которое извлекает изображение из веб-службы. Веб-служба встроила некоторые метаданные в изображение перед отправкой на клиент C#.C# Image.FromStream(): потерянные метаданные при работе в Windows 8/10

Это часть метода. Он извлекает поток из объекта Response и создает изображение из потока. Обратите внимание, что я использую System.Drawing.Image, а не System.Windows.Controls.Image - это означает, что я не могу использовать какой-либо ImageSource или BitmapSource.

System.Drawing.Image img = null; 
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) 
{ 
    Stream stream = response.GetResponseStream(); 
    img = System.Drawing.Image.FromStream(stream); 
    ....... 
} 
return img; 

Изображение выглядит прекрасно, но внутри встроены метаданные. Изображение находится в формате PNG, и есть другой способ, который извлекает информацию из Image. В общей сложности шесть встроенных метаданных. Формат PNG (фрагменты PNG) описывается here. Данные сохраняются в разделе «tEXt».

public static Hashtable GetData(Image image) 
{ 
    Hashtable metadata = null; 
    data = new Hashtable(); 

    byte[] imageBytes; 
    using (MemoryStream stream = new MemoryStream()) 
    { 
     image.Save(stream, image.RawFormat); 
     imageBytes = new byte[stream.Length]; 
     imageBytes = stream.ToArray(); 
    } 

    if (imageBytes.Length <= 8) 
    { 
     return null; 
    } 

    // Skipping 8 bytes of PNG header 
    int pointer = 8; 

    while (pointer < imageBytes.Length) 
    { 
     // read the next chunk 
     uint chunkSize = GetChunkSize(imageBytes, pointer); 
     pointer += 4; 
     string chunkName = GetChunkName(imageBytes, pointer); 
     pointer += 4; 

     // chunk data ----- 
     if (chunkName.Equals("tEXt")) 
     { 
      byte[] data = new byte[chunkSize]; 
      Array.Copy(imageBytes, pointer, data, 0, chunkSize); 
      StringBuilder stringBuilder = new StringBuilder(); 
      foreach (byte t in data) 
      { 
       stringBuilder.Append((char)t); 
      } 

      string[] pair = stringBuilder.ToString().Split(new char[] { '\0' }); 
      metadata[pair[0]] = pair[1]; 
     } 

     pointer += (int)chunkSize + 4; 

     if (pointer > imageBytes.Length) 
      break; 
    } 
    return data; 
} 

private static uint GetChunkSize(byte[] bytes, int pos) 
{ 
    byte[] quad = new byte[4]; 
    for (int i = 0; i < 4; i++) 
    { 
     quad[3 - i] = bytes[pos + i]; 
    } 

    return BitConverter.ToUInt32(quad); 
} 

private static string GetChunkName(byte[] bytes, int pos) 
{ 
    StringBuilder builder = new StringBuilder(); 
    for (int i = 0; i < 4; i++) 
    { 
     builder.Append((char)bytes[pos + i]); 
    } 

    return builder.ToString(); 
} 

В Windows 7 обнаружены и извлечены все шесть частей метаданных. Короче говоря, в среде Windows 7 мне удалось получить все, что мне нужно.

Когда я переношу это на терминал Windows 10 (также пробовал Windows 8), все становится по-другому. Я могу извлечь только 2 части метаданных из Image.

Поскольку мой метод GetData() преобразует Image в byte[], поэтому я попытался извлечь данные прямо из потока веб-службы. Я преобразовал поток в byte[] и использовал тот же метод для извлечения метаданных из byte[]. С помощью этого метода мне удалось вернуть все 6 метаданных.

Вопрос в следующем: Что изменилось? Он отлично работает в Windows 7, но не так в Windows 8 и 10. Я все еще могу вернуть данные, если я не превращу поток в Image. Где-то в этом процессе метаданные теряются. Он либо теряется, когда я конвертирую поток в Image, или когда я конвертирую Image обратно в byte[]. В качестве примечания я попытался преобразовать byte[] в строку. Строковое представление byte[] из потока отличается от byte[] от Image. Используя правильный кодировщик, я мог видеть 4 метаданных, отсутствующих в последнем byte[].

+0

Можете ли вы предоставить образец изображения с метаданными? –

+0

@GeorgeVovos Укажите, в какой форме ..? – Jai

+0

Являются ли байты, созданные потоком изображения.ToArray() одинаково из одной версии Windows в другую? (обратите внимание, что вы можете удалить предыдущее выделение, как это делает ToArray). В противном случае просто убедитесь, что вы передаете непрозрачные массивы байтов и не используете объект промежуточного изображения. Кроме того, ваше преобразование байтов -> строк для блока tEXt неверно, поскольку текстовое кодирование соответствует ISO-8859-1 для спецификации PNG. Вы должны что-то вроде этого: var text = Encoding.GetEncoding («ISO-8859-1»). GetString (bytes) 'вместо –

ответ

2

тексте метаданные: представлена ​​в ISO/IEC 8859-1

Попробуйте добавить следующее, прежде чем сделать запрос:

request.Headers.Add(HttpRequestHeader.AcceptCharset, "ISO-8859-1"); 

так, модифицировать код:

System.Drawing.Image img = null; 

//accept Charset "ISO-8859-1" 
request.Headers.Add(HttpRequestHeader.AcceptCharset, "ISO-8859-1"); 

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) 
{ 
Stream stream = response.GetResponseStream(); 
img = System.Drawing.Image.FromStream(stream); 
    ....... 
} 
return img; 

только для информации, можете ли вы опубликовать, что такое окна EncodingName в окнах 7/8/10

использовать команду PowerShell, чтобы узнать:

[System.Text.Encoding]::Default.EncodingName 

Edit:

Я рассмотрел исходный код Dotnet System.Drawing.Image.FromStream и обнаружили, что утверждение:

// [Obsolete("Use Image.FromStream(stream, useEmbeddedColorManagement)")] 
    public static Image FromStream(Stream stream) { 
     return Image.FromStream(stream, false); 
    } 

пытаются использовать:

Image.FromStream(stream, true); 
    or 
Image.FromStream(stream, true,true); 

Подробности параметров:

public static Image FromStream(
    Stream stream, 
    bool useEmbeddedColorManagement,////true to use color management information embedded in the data stream; otherwise, false. 
    bool validateImageData //true to validate the image data; otherwise, false. 
) 

Image.FromStream Method

Изменить 2:

Я сделал эксперимент на файл изображения PNG с данными TEXT:

я разработал функцию, чтобы измерить размер изображения в байтах, который считывается с помощью функции FromStream() и я выполнил как на win7/выигрыше 10.

в приведенной ниже таблице, представляют собой реальный размер изображения в байтах и ​​в среде:

The file size: 502,888 byte (real size on disk).  

win 7   win10  function used 
569674  597298  Image.FromStream(stream, true,true) 
597343  597298  Image.FromStream(stream, true) 
597343  597298  Image.FromStream(stream, false) 

Вы обнаружите, что размер отличается как в окружающей среде и отличается реальный размер в диске ,

Итак, вы ожидаете, что позиция мета данных изменяется (но не потерял, только перераспределяются)

Я использовал шестнадцатеричный инструмент Редактор для просмотра TText кусок.

ТЕКСТ находится в позиции 66 (в десятичной форме), начиная с начала файла, и это то же самое на обеих средах !!!

Я использовал свою собственную функцию чтения метаданных, и результат будет таким же и действителен как для окон 7, так и для окон 10 (НЕТ ПОТЕРИ ДАННЫХ).

Официальный сайт для формата PNG является: https://www.w3.org/TR/PNG/

Заключения

Функция Image.FromStream не подходит для чтения метаданных, файл изображения должны быть прочитан в необработанном формате байт не в так как функция FromStream перераспределяет необработанные данные таким образом, чтобы сохранить изображение и его данные без искажений (то есть внутренности функции в dotnet).

Чтобы прочитать метаданные, как описано в спецификациях PNG, вы должны прочитать поток в RAW BYTES с самого начала файла, как описано в спецификациях.

Я совет вам использовать библиотеки классов MetadataExtractor читать мета-данные, и его результатом является очень точным в обоих окнах 7 и Windows 10

Вы можете установить библиотеку из NuGet. установки-пакет MetadataExtractor

Edit 3: Предлагаемое решение

Теперь проблема решена, и следующий класс действителен как для Win 7, выиграть 8

Основное изменение чтения файл изображения, как необработанные байты

class MetaReader 
{ 
    public static Hashtable GetData(string fname) 
    { 
     using (FileStream image = new FileStream(fname, FileMode.Open, FileAccess.Read)) 
     { 
      Hashtable metadata = new Hashtable(); 
      byte[] imageBytes; 

      using (var memoryStream = new MemoryStream()) 
      { 
       image.CopyTo(memoryStream); 
       imageBytes = memoryStream.ToArray(); 
       Console.WriteLine(imageBytes.Length); 
      } 

      if (imageBytes.Length <= 8) 
      { 
       return null; 
      } 

      // Skipping 8 bytes of PNG header 
      int pointer = 8; 

      while (pointer < imageBytes.Length) 
      { 
       // read the next chunk 
       uint chunkSize = GetChunkSize(imageBytes, pointer); 
       pointer += 4; 
       string chunkName = GetChunkName(imageBytes, pointer); 
       pointer += 4; 

       // chunk data ----- 
       if (chunkName.Equals("tEXt")) 
       { 
        byte[] data = new byte[chunkSize]; 
        Array.Copy(imageBytes, pointer, data, 0, chunkSize); 
        StringBuilder stringBuilder = new StringBuilder(); 
        foreach (byte t in data) 
        { 
         stringBuilder.Append((char)t); 
        } 

        string[] pair = stringBuilder.ToString().Split(new char[] { '\0' }); 
        metadata[pair[0]] = pair[1]; 
        Console.WriteLine(metadata[pair[0]]); 
       } 

       pointer += (int)chunkSize + 4; 

       if (pointer > imageBytes.Length) 
        break; 
      } 
      return metadata; 
     } 
    } 

    private static uint GetChunkSize(byte[] bytes, int pos) 
    { 
     byte[] quad = new byte[4]; 
     for (int i = 0; i < 4; i++) { quad[3 - i] = bytes[pos + i]; } 

     return BitConverter.ToUInt32(quad, 0); 

    } 

    private static string GetChunkName(byte[] bytes, int pos) 
    { 
     StringBuilder builder = new StringBuilder(); for (int i = 0; i < 4; i++) { builder.Append((char)bytes[pos + i]); } 

     return builder.ToString(); 

    } 
} 

Чтение метаданных с веб-службы:

Вы можете загрузить файл изображения с URL-адреса в виде потока и прочитать метаданные «на лету». Кроме того, вы можете создать экземпляр System.Drawing.Image и делать то, что когда-либо обрабатывалось на изображении. Вы можете найти полную демо с исходным кодом по адресу:

Reading Metadata from PNG loaded from Web Stream -TryIt

+0

Извините за то, что вы ушли после того, как выставили щедрость. Я просто попытался, и это не сработало. Кроме того, я мог бы получить метаданные прямо из потока ответов в Интернете. Но как только он преобразуется в экземпляр 'System.Drawing.Image', метаданные теряются. – Jai

+0

В любом случае кодировка по умолчанию для Windows 7 и 10 - «Западная Европа (Windows)». – Jai

+0

Хорошие результаты там. Какую функцию чтения метаданных вы использовали? Это просто библиотека MetadataExtractor? – Jai

 Смежные вопросы

  • Нет связанных вопросов^_^