将元数据写入MemoryStream时,JpegBitmapEncoder.Save()会抛出exception

我试图在JPG图像上设置没有它的元数据。 在这种情况下,您不能使用就地编写器(InPlaceBitmapMetadataWriter),因为图像中没有元数据的位置。

如果我使用FileStream作为输出 – 一切正常。 但是如果我尝试使用MemoryStream作为输出–JpegBitmapEncoder.Save()会抛出exception(来自HRESULT的exception:0xC0000005)。 经过一番调查后,我还发现如果我提供的是null而不是元数据,那么编码器可以将图像保存到内存流中。

我已经做了一个非常简化和简短的例子来重现问题:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Media.Imaging; namespace JpegSaveTest { class Program { public static JpegBitmapEncoder SetUpMetadataOnStream(Stream src, string title) { uint padding = 2048; BitmapDecoder original; BitmapFrame framecopy, newframe; BitmapMetadata metadata; JpegBitmapEncoder output = new JpegBitmapEncoder(); src.Seek(0, SeekOrigin.Begin); original = JpegBitmapDecoder.Create(src, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); if (original.Frames[0] != null) { framecopy = (BitmapFrame)original.Frames[0].Clone(); if (original.Frames[0].Metadata != null) metadata = original.Frames[0].Metadata.Clone() as BitmapMetadata; else metadata = new BitmapMetadata("jpeg"); metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", padding); metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", padding); metadata.SetQuery("/xmp/PaddingSchema:Padding", padding); metadata.SetQuery("System.Title", title); newframe = BitmapFrame.Create(framecopy, framecopy.Thumbnail, metadata, original.Frames[0].ColorContexts); output.Frames.Add(newframe); } else { Exception ex = new Exception("Image contains no frames."); throw ex; } return output; } public static MemoryStream SetTagsInMemory(string sfname, string title) { Stream src, dst; JpegBitmapEncoder output; src = File.Open(sfname, FileMode.Open, FileAccess.Read, FileShare.Read); output = SetUpMetadataOnStream(src, title); dst = new MemoryStream(); output.Save(dst); src.Close(); return (MemoryStream)dst; } static void Main(string[] args) { string filename = "Z:\\dotnet\\gnom4.jpg"; MemoryStream s; s = SetTagsInMemory(filename, "test title"); } } } 

这是简单的控制台应用程序 要运行它,请将文件名变量内容替换为没有元数据的任何.jpg文件的路径(或使用我的 )。

Ofc我可以先将图像保存到临时文件中,关闭它,然后打开并复制到MemoryStream,但它太脏又慢的解决方法。 欢迎任何关于让这项工作的想法:)

如果有人遇到同样的问题,这里是解决方案:

如果从主应用程序线程尝试.Save()jpeg,请在Main()之前添加[STAThread]。

如果没有,请为调用JpegBitmapEncoder.Save()的线程调用.SetApartmentState(ApartmentState.STA)

WinXP和WinVista版本的windowscodecs.dll不可重新输入,因此如果你将使用默认的MTA模型(它是.NET Framework 2.0的默认模式)用于调用JpegBitmapEncoder.Save()函数的线程,它会表现得很奇怪并抛出描述的exception。 Win7版本的windowscodecs.dll没有这个问题。

我运行你的代码没有修改,它没有抛出错误。 我甚至尝试将修改后的数据保存到磁盘,图像本身没有损坏。

 string filename = "e:\\a.jpg"; MemoryStream s; s = SetTagsInMemory(filename, "test title"); FileStream fs = new FileStream("e:\\b.jpg", FileMode.CreateNew, FileAccess.ReadWrite); BinaryWriter sw = new BinaryWriter(fs); s.Seek(0, SeekOrigin.Begin); while (s.Position < s.Length) { byte[] data = new byte[4096]; s.Read(data, 0, data.Length); sw.Write(data); } sw.Flush(); sw.Close(); fs.Close(); 

除了我在s = SetTagsInMemory(...)下面添加的写入磁盘之外,其余代码都是未修改的。

编辑:哦,metadeta肯定会在新文件中结束,前一个没有我能看到的任何元数据。