这是该方法的一部分.它从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格式,还有一种方法可以从图像中提取信息.共有六块元数据嵌入. 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,chunkSize); StringBuilder stringBuilder = new StringBuilder(); foreach (byte t in data) { stringBuilder.Append((char)t); } string[] pair = stringBuilder.ToString().Split(new char[] { 'request.headers.Add(httpRequestheader.AcceptCharset,"ISO-8859-1");' }); 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个元数据.
因为我的GetData()方法将Image转换为byte [],所以我尝试从Web服务流中提取数据.我将流转换为byte [],并使用相同的技术从byte []中提取元数据.我设法使用这种方法获取所有6个元数据.
所以问题是:有什么变化?它在windows 7中工作得很好,但在windows 8和10中不是这样.我仍然可以收回数据,前提是我不将流转换成图像.在此过程中,元数据丢失.当将流转换为图像时,或将图像转换为byte []时,丢失.作为附注,我已经尝试将byte []转换为字符串.来自流的字节[]的字符串表示与图像中的字节[]不同.使用正确的编码器,我可以看到稍后的字节[]中丢失了4个元数据.
解决方法 元数据tEXt:在ISO / IEC 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;
所以修改你的代码:
[System.Text.EnCoding]::Default.EnCodingname
只是为了信息,你可以发布windows 7/8/10中的windows EnCodingname是什么
使用powershell命令知道:
// [Obsolete("Use Image.FromStream(stream,useEmbeddedcolorManagement)")] public static Image FromStream(Stream stream) { return Image.FromStream(stream,false); }
编辑:
我查看了DOTNet System.Drawing.Image.FromStream的源代码
并发现声明:
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. )
有关参数的详细信息:
The file size: 502,888 byte (real size on disk). win 7 win10 function used 569674 597298 Image.FromStream(stream,true) 597343 597298 Image.FromStream(stream,false)
Image.FromStream Method
编辑2:
我用TEXT数据对PNG图像文件进行了实验:
我开发了一个函数来测量图像的大小(以字节为单位),这是通过函数FromStream()读取的,我在win7 / win10上执行.
下表显示了两个环境中图像的实际大小(以字节为单位):
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,chunkSize); StringBuilder stringBuilder = new StringBuilder(); foreach (byte t in data) { stringBuilder.Append((char)t); } string[] pair = stringBuilder.ToString().Split(new char[] { '' }); 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(); }}
你发现两个环境的大小是不同的,不同于
磁盘中的实际大小.
所以,您期望元数据的位置已更改(但不会丢失,仅重新分配)
我使用十六进制编辑器工具来查看tTEXT块.
tEXT位于第66位(十进制),从文件开始,两者都是一样的!
我使用我自己的元数据读取器功能,结果对于windows 7或windows 10(没有数据丢失)是相同和有效的.
PNG格式的官方网站是:https://www.w3.org/TR/PNG/
结论
Image.FromStream函数不适合读取元数据,图像文件应以原始字节格式读取,而不是以图像格式读取,因为FromStream函数会将原始数据重新分配,以保持图像及其数据不变形(即内部函数在dotnet中).
要按照PNG规范的描述读取元数据,您应该按照规范从描述文件开始读取RAW BYTES中的流.
我建议您使用类库MetadataExtractor来读取元数据,其结果在windows 7和windows 10中都非常准确
您可以从nuget安装库.
install-Package MetadataExtractor
编辑3:建议的解决方案
现在问题解决了,下面的类对win 7,win 8都有效
主要的变化是将图像文件读为原始字节
从Web服务读取元数据:
您可以从url加载图像文件作为流,并且即时读取元数据.
此外,您可以创建System.Drawing.Image的实例,并对图像进行处理.
您可以在以下位置找到源代码的完整演示:
Reading Metadata from PNG loaded from Web Stream -TryIt
总结以上是内存溢出为你收集整理的C#Image.FromStream():在Windows 8/10中运行时丢失元数据全部内容,希望文章能够帮你解决C#Image.FromStream():在Windows 8/10中运行时丢失元数据所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)