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

时间:2010-04-24 17:19:13

标签: c# wpf metadata exif

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

如果我使用FileStream作为输出 - 一切正常。但是如果我尝试使用MemoryStream作为输出 - JpegBitmapEncoder.Save()会抛出异常(来自HRESULT的异常: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文件的路径(或使用mine)。

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

2 个答案:

答案 0 :(得分:2)

如果有人遇到同样的问题,可以采用以下解决方案:

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

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

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

答案 1 :(得分:1)

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

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肯定会在新文件中结束,前一个没有我能看到的任何元数据。