Я создаю приложение рабочего стола 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 было необработанным", выделяя эту строку:
encoder.Save(memstream);
Необработанное исключение типа "System.Runtime.InteropServices.COMException" произошло в PresentationCore.dll
Дополнительная информация: дескриптор недействителен. (Исключение из HRESULT: 0x80070006 (E_HANDLE))
Я видел здесь, что это может быть связано с проблемой потоковой передачи, поэтому вместо прямого вызова 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, создав локальный temp file--, который ничего не изменил:
FileStream memstream = new FileStream(System.IO.Path.GetDirectoryName(newPath) + @"\" + "temp.jpg", System.IO.FileMode.OpenOrCreate);
Есть идеи?
В коде есть несколько ошибок.
Исходный поток должен храниться открытым до тех пор, пока битмап -фрагмент не будет записан в целевой поток.
Метаданные изображения BitmapFrame из BitmapDecoder доступны только для чтения. Вы должны создать новый BitmapFrame из оригинала, если хотите изменить метаданные.
Третий запрос, кажется, сломан. Исключение означает, что "Свойство не может быть найдено".
Этот код работает для меня:
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);
}
}
}
Вы получили исключение, потому что вы закрыли поток, используемый для загрузки исходного jpeg. Комментируйте первый поток.Close() (чуть выше if (setCaption)), и он будет работать. Точно так же, как при работе с экземпляром Image, и вы должны держать поток открытым для жизни Image.