【发布时间】:2016-12-06 04:56:37
【问题描述】:
我有一个从 Web 服务检索图像的应用程序。 Web 服务会在发送到 C# 客户端之前将一些元数据嵌入到图像中。
这是方法的一部分。它从 Response 对象中检索 Stream,并从流中创建一个 Image。请注意,我使用的是 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)时,情况就不同了。我只能从 Image 中提取 2 条元数据。
因为我的GetData() 方法将Image 转换为byte[],所以我尝试直接从Web 服务流中提取数据。我将流转换为byte[],并使用相同的技术从byte[] 中提取元数据。我设法用这种方法取回了所有 6 个元数据。
所以问题是:发生了什么变化?它在 Windows 7 中完全可以正常工作,但在 Windows 8 和 10 中却不是这样。只要我不转,我仍然可以取回数据流到Image。在此过程中的某个地方,元数据会丢失。当我将流转换为Image 或将Image 转换回byte[] 时,它要么丢失。作为旁注,我尝试将byte[] 转换为字符串。流中byte[] 的字符串表示形式看起来与Image 中的byte[] 不同。使用正确的编码器,我可以看到后面的byte[] 中缺少 4 个元数据。
【问题讨论】:
-
你能提供一个带有元数据的示例图像吗?
-
@GeorgeVovos 以什么形式提供..?
-
image stream.ToArray() 生成的字节在一个 Windows 版本到另一个版本中是否相同? (请注意,您可以像 ToArray 那样删除之前的分配)。否则,只需确保传递不透明的字节数组,并且不要使用中间图像对象。此外,您的字节 - > tEXt 块的字符串转换不正确,因为每个 PNG 规范的文本编码是 ISO-8859-1。你应该像这样
var text = Encoding.GetEncoding("ISO-8859-1").GetString(bytes)而不是 -
@Jai 你能提供一个带有元数据的 png 图像吗
-
能否提供 GetChunkSize 的源代码,GetChunkName.BTW,data 变量声明了两次,VS 发出警告。
标签: c# image web-services png platform