2014-06-11 50 views
1

我正在構建一個WPF桌面應用程序,以幫助我組織照片發佈到Facebook。下面是我用的標題(EXIF + IPTC + XMP)創建一個新位置的照片副本程式碼:JpegBitmapEncoder.Save()在使用元數據編寫圖像時引發異常

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是未處理」異常突出顯示該行:

encoder.Save(memstream); 

'System.Runtime.InteropServices.COMException' 類型的未處理的異常出現在 PresentationCore.dll中

其他信息:句柄無效。 (異常來自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創建本地臨時file--並沒有改變任何東西:

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

任何想法?

回答

3

你的代碼有一些錯誤。

  1. 源流必須保持打開狀態,直到將BitmapFrame寫入目標流爲止。

  2. 來自BitmapDecoder的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); 
     } 
    } 
} 
+0

太好了,謝謝! – amb9800

1

由於您關閉了用於加載原始jpeg的流,因此您得到了異常。評論第一個stream.Close()(就像上面的(setCaption)),它會工作。就像處理Image實例一樣,您必須在圖像的整個生命週期中保持該流打開。

+0

使用BitmapCacheOption.OnLoad,而不是無使代碼運行速度減慢10倍以上(圍繞每個1M 100個圖像測試)。 – cdel