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