Сохранить BitmapImage в файл - PullRequest
21 голосов
/ 12 ноября 2010

Я работаю над программой, которая загружает изображения с URL-адреса в битовый образ и отображает его.Затем я пытаюсь сохранить растровое изображение на жесткий диск с помощью jpegbitmapencoder.Файл успешно создан, но фактическое изображение в формате JPEG пустое или 1 черный пиксель.

public Guid SavePhoto(string istrImagePath)
{
    ImagePath = istrImagePath;

    BitmapImage objImage = new BitmapImage(
        new Uri(istrImagePath, UriKind.RelativeOrAbsolute));
    PictureDisplayed.Source = objImage;
    savedCreationObject = objImage;

    Guid photoID = System.Guid.NewGuid();
    string photolocation = photoID.ToString() + ".jpg";  //file name

    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(objImage));

    using (FileStream filestream = new FileStream(photolocation, FileMode.Create))
    {
        encoder.Save(filestream);
    }

    return photoID;
}

Это функция, которая сохраняет и отображает фотографию.Фотография отображается правильно, но снова при сохранении я получаю пустой JPEG или 1 черный пиксель.

Ответы [ 2 ]

30 голосов
/ 12 ноября 2010

Когда вы создаете BitmapImage из Uri, для загрузки изображения требуется время.

Если вы проверите следующее свойство, значение, скорее всего, будет TRUE

objImage.IsDownloading

.вы значительно присоединяете слушателя к обработчику событий DownloadCompleted и перемещаете всю обработку в этот EventHandler.

objImage.DownloadCompleted += objImage_DownloadCompleted;

Где этот обработчик будет выглядеть примерно так:

private void objImage_DownloadCompleted(object sender, EventArgs e)
{
  JpegBitmapEncoder encoder = new JpegBitmapEncoder();
  Guid photoID = System.Guid.NewGuid();
  String photolocation = photoID.ToString() + ".jpg";  //file name 

  encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));

  using (var filestream = new FileStream(photolocation, FileMode.Create))
    encoder.Save(filestream);
} 

Возможно, вы также захотитедобавить еще один EventHandler для DownloadFailed, чтобы корректно обрабатывать любые ошибки.

Редактировать

Добавлен полный пример класса на основе комментария Бена:

public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();

    SavePhoto("http://www.google.ca/intl/en_com/images/srpr/logo1w.png");
  }

  public void SavePhoto(string istrImagePath)
  {
    BitmapImage objImage = new BitmapImage(new Uri(istrImagePath, UriKind.RelativeOrAbsolute));

    objImage.DownloadCompleted += objImage_DownloadCompleted;
  }

  private void objImage_DownloadCompleted(object sender, EventArgs e)
  {
    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
    Guid photoID = System.Guid.NewGuid();
    String photolocation = photoID.ToString() + ".jpg";  //file name 

    encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender));

    using (var filestream = new FileStream(photolocation, FileMode.Create))
      encoder.Save(filestream);
  } 
}
5 голосов
/ 03 апреля 2015

Расширяя решение Криса Бакстера, этот конвертер использует локальную версию, если она существует, в противном случае загружает ее и сохраняет файл.

using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;

namespace MyNamespace
{
    public sealed class UriToCachedImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var url = value as string;
            if (url == null)
                return null;

            var webUri = new Uri(url, UriKind.Absolute);
            var filename = Path.GetFileName(webUri.AbsolutePath);

            var localFilePath = Path.Combine("C:\\MyImagesFolder\\", filename);

            if (File.Exists(localFilePath))
            {
                return BitmapFrame.Create(new Uri(localFilePath, UriKind.Absolute));
            }

            var image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = webUri;
            image.EndInit();

            SaveImage(image, localFilePath);

            return image;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public void SaveImage(BitmapImage image, string localFilePath)
        {
            image.DownloadCompleted += (sender, args) =>
            {
                var encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create((BitmapImage) sender));
                using (var filestream = new FileStream(localFilePath, FileMode.Create))
                {
                    encoder.Save(filestream);
                }
            };
        }
    }
}

И убедитесь, что у вас есть доступ к конвертеру в xaml

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:u="clr-namespace:MyNamespace"          
             d:DesignHeight="500" 
             d:DesignWidth="420">
    <UserControl.Resources>
        <ResourceDictionary>
            <u:UriToCachedImageConverter x:Key="UrlToCachedImageConverter" />
        </ResourceDictionary>
    </UserControl.Resources>            
</UserControl>

И использовать конвертер на изображении

<Image Source="{Binding URL, Mode=OneWay, Converter={StaticResource UrlToCachedImageConverter}, IsAsync=true}"/>
...