Skip to main content
 首页 » 编程设计

c#之JpegBitmapEncoder.Save() 在使用元数据写入图像时抛出异常

2024年02月06日51三少

我正在构建一个 WPF 桌面应用程序来帮助我组织照片以发布到 Facebook。这是我在新位置创建照片副本并添加了标题 (EXIF + IPTC + XMP) 的代码:

    private void SaveImageAs(string currPath, string newPath, bool setCaption = false, string captionToSet = "") 
    { 
        System.IO.FileStream stream = new System.IO.FileStream(currPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 
        JpegBitmapDecoder decoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.None); 
        BitmapFrame bitmapFrame = decoder.Frames[0]; 
        BitmapMetadata metadata = bitmapFrame.Metadata.Clone() as BitmapMetadata; 
        stream.Close(); 
 
        if (setCaption) 
        { 
            // if we want to set the caption, do it in EXIF, IPTC, and XMP 
 
            metadata.SetQuery("/app1/ifd/{uint=270}", captionToSet); 
            metadata.SetQuery("/app13/irb/8bimiptc/iptc/Caption", captionToSet); 
            metadata.SetQuery("/xmp/dc:description/x-default", captionToSet); 
        } 
 
        MemoryStream memstream = new MemoryStream();   // create temp storage in memory 
        JpegBitmapEncoder encoder = new JpegBitmapEncoder(); 
        encoder.Frames.Add(BitmapFrame.Create(bitmapFrame, bitmapFrame.Thumbnail, metadata, bitmapFrame.ColorContexts)); 
        encoder.Save(memstream);   // save in memory 
        stream.Close(); 
 
        stream = new FileStream(newPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); 
        memstream.Seek(0, System.IO.SeekOrigin.Begin);    // go to stream start 
        byte[] bytes = new byte[memstream.Length + 1]; 
        memstream.Read(bytes, 0, (int)memstream.Length); 
        stream.Write(bytes, 0, bytes.Length); 
        stream.Close(); 
        memstream.Close(); 
    } 

运行它,我得到一个“COMException was unhandled”异常突出显示这一行:

encoder.Save(memstream); 

An unhandled exception of type 'System.Runtime.InteropServices.COMException' occurred in PresentationCore.dll

Additional information: The handle is invalid. (Exception from HRESULT: 0x80070006 (E_HANDLE))

我看到了here这可能是由于线程问题,所以我没有直接从应用程序调用 SaveImageAs,而是添加了这个,但没有效果:

        private void _SaveImageAs(string currPath, string newPath, bool setCaption = false, string captionToSet = "") 
    { 
        Thread saveThread = new Thread(() => SaveImageAs(currPath, newPath, setCaption, captionToSet)); 
        saveThread.SetApartmentState(ApartmentState.STA); 
        saveThread.IsBackground = false; 
        saveThread.Start(); 
    } 

我还尝试将 MemoryStream 换成 FileStream,创建一个本地临时文件——这并没有改变任何东西:

FileStream memstream = new FileStream(System.IO.Path.GetDirectoryName(newPath) + @"\" + "temp.jpg", System.IO.FileMode.OpenOrCreate); 

有什么想法吗?

请您参考如下方法:

您的代码中有一些错误。

  1. 源流必须保持打开状态,直到将 BitmapFrame 写入目标流。

  2. 来自 BitmapDecoder 的 BitmapFrame 的图像元数据是只读的。当您想要修改元数据时,您必须从原始的 BitmapFrame 创建一个新的 BitmapFrame。

  3. 第三个查询似乎已损坏。异常显示“找不到属性”。

这段代码对我有用:

public static void SaveImageAs(string sourcePath, string targetPath, string caption) 
{ 
    using (var sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 
    { 
        var decoder = new JpegBitmapDecoder(sourceStream, BitmapCreateOptions.None, BitmapCacheOption.None); 
        var frame = decoder.Frames[0]; 
 
        if (!string.IsNullOrWhiteSpace(caption)) 
        { 
            frame = BitmapFrame.Create(frame); 
            var metadata = (BitmapMetadata)frame.Metadata; 
 
            metadata.SetQuery("/app1/ifd/{uint=270}", caption); 
            metadata.SetQuery("/app13/irb/8bimiptc/iptc/Caption", caption); 
            //metadata.SetQuery("/xmp/dc:description/x-default", caption); 
        } 
 
        var encoder = new JpegBitmapEncoder(); 
        encoder.Frames.Add(frame); 
 
        using (var targetStream = new FileStream(targetPath, FileMode.Create)) 
        { 
            encoder.Save(targetStream); 
        } 
    } 
}