web-dev-qa-db-fra.com

Enregistrer l'image bitmap dans un fichier

Je travaille sur un programme qui télécharge des images d'une URL vers une image bitmap et l'affiche. Ensuite, j'essaie d'enregistrer l'image bitmap sur le disque dur à l'aide de jpegbitmapencoder. Le fichier est créé avec succès mais l'image jpeg réelle est vide ou 1 pixel noir.

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;
}

Il s'agit de la fonction qui enregistre et affiche la photo. La photo s'affiche correctement mais encore une fois enregistrée, j'obtiens un jpeg vide ou 1 pixel noir.

21
Ben Unsworth

Lorsque vous créez votre BitmapImage à partir d'un Uri, il faut du temps pour télécharger l'image.

Si vous cochez la propriété suivante, la valeur sera probablement TRUE

objImage.IsDownloading

En tant que tel, vous attachez beaucoup un écouteur au gestionnaire d'événements DownloadCompleted et déplacez tous les traitements vers ce gestionnaire d'événements.

objImage.DownloadCompleted += objImage_DownloadCompleted;

Où ce gestionnaire ressemblera à quelque chose comme:

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);
} 

Vous souhaiterez probablement également ajouter un autre EventHandler pour DownloadFailed afin de gérer avec élégance tous les cas d'erreur.

Modifier

Ajout d'un exemple de classe complet basé sur le commentaire de Ben:

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);
  } 
}
30
Chris Baxter

Développant la solution de Chris Baxter, ce convertisseur utilise la version locale si elle existe, sinon la télécharge et enregistre le fichier.

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);
                }
            };
        }
    }
}

Et assurez-vous que vous pouvez accéder au convertisseur dans votre 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>

Et utilisez le convertisseur sur une image

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