【问题标题】:JpegBitmapEncoder.Save() throws exception when writing image with metadata to MemoryStream将带有元数据的图像写入 MemoryStream 时,JpegBitmapEncoder.Save() 引发异常
【发布时间】:2010-04-25 06:58:58
【问题描述】:

我正在尝试在没有它的 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)。

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

【问题讨论】:

  • 不是我第一次犯错。很抱歉!
  • Nps,感谢您的帮助。

标签: c# wpf metadata exif


【解决方案1】:

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

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

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

WinXP 和 WinVista 版本的 windowscodecs.dll 是不可重新输入的,因此如果您将使用默认的 MTA 模型(自 .NET framework 2.0 起为默认)调用 JpegBitmapEncoder.Save() 函数的线程,它可能会出现异常并抛出描述例外。 Win7版本的windowscodecs.dll没有这个问题。

【讨论】:

    【解决方案2】:

    我没有修改就运行了您的代码,并且没有引发错误。 我什至尝试将修改后的数据保存到磁盘,并且图像本身没有损坏。

    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(...) 下添加的写入磁盘之外,您的其余代码均未修改。

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

    【讨论】:

    • 这有点神秘 oO 你能把你的整个解决方案(包括所有 VS 文件)归档并邮寄给我吗? mephisto123[at]gmail[dot]com 我会尝试运行你的,看看会发生什么。谢谢。
    • 把你的 a.jpg 也发给我。或测试程序是否适用于我在第一篇文章中链接的 jpg。
    • 是的。当然,你运行的是什么版本的VS?我做了一个 2010 年的项目,但如果它更适合你,我可以制作一个 2008 年的项目。
    • 我也尝试使用您的图像运行它并且它有效。无论如何,我会把它压缩并发送给你整个东西,包括你的包含元数据的图像。
    • 我用的是 2008。其实我想知道它在 2008 年是否适合你
    猜你喜欢
    • 2014-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 2021-09-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-19
    相关资源
    最近更新 更多