JpegBitmapEncoder.Save () генерирует исключение при записи изображения с метаданными

1

Я создаю приложение рабочего стола 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);

Есть идеи?

Теги:
wpf
metadata
exif
iptc

2 ответа

3
Лучший ответ

В коде есть несколько ошибок.

  1. Исходный поток должен храниться открытым до тех пор, пока битмап -фрагмент не будет записан в целевой поток.

  2. Метаданные изображения BitmapFrame из BitmapDecoder доступны только для чтения. Вы должны создать новый 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);
        }
    }
}
1

Вы получили исключение, потому что вы закрыли поток, используемый для загрузки исходного jpeg. Комментируйте первый поток.Close() (чуть выше if (setCaption)), и он будет работать. Точно так же, как при работе с экземпляром Image, и вы должны держать поток открытым для жизни Image.

  • 0
    Использование BitmapCacheOption.OnLoad вместо None заставляет код работать более чем в 10 раз медленнее (проверено на 100 изображениях размером около 1 М каждый).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню