从Zip档案中读取BitmapImage

时间:2018-02-09 08:20:34

标签: c# wpf zip bitmapimage

对于WPF项目,我需要从Zip文件中读取BitmapImage。原始文件格式是.png我知道如何直接从文件系统执行此操作,并且工作正常。不幸的是,从Zip文件中可以看不到图像,但图像似乎已被读取。

我创建了一个简单的测试项目:

using System;
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO.Compression;

namespace BitImageTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
    {

    public BitmapImage RouteImage { get; set; }

    public MainWindow()
        {
        InitializeComponent();
        LoadBitmap();
        DataContext = RouteImage;
        }

    public void LoadBitmap()
        {
        RouteImage = new BitmapImage();
        var PackedFile = @"D:\Games\steamapps\common\RailWorks\Content\Routes\00000036-0000-0000-0000-000000002012\MainContent.ap";
        try
            {
                {
                using (ZipArchive archive = ZipFile.OpenRead(PackedFile))
                    {
                    var file = archive.GetEntry("RouteInformation/image.png");
                    if (file != null)
                        {
                        using (var zipEntryStream = file.Open())
                            {
                            RouteImage.BeginInit();
                            RouteImage.CacheOption = BitmapCacheOption.OnLoad;
                            RouteImage.StreamSource = zipEntryStream;
                            RouteImage.EndInit();
                            return;
                            }
                        }
                    }
                }
            }
        catch (Exception e)
            {
            var s = "Exception: " + e.Message;
            }
        }
    }
}

XAML代码如下所示:

<Window x:Class="BitImageTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:BitImageTest"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Image Height="128" Width="256"  Source="{Binding BitmapImage}"/>
</Grid>

在调试器中,它看起来像是创建流并绑定到BitmapImage,但宽度和高度设置为1.所以我认为读取zip文件中的数据有些不对。

2 个答案:

答案 0 :(得分:2)

不确定确切原因,但似乎有必要将zip流复制到中间MemoryStream并从那里读取BitmapImage。

您还应该编写一个包含属性更改通知的视图模型类:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private BitmapImage routeImage;

    public BitmapImage RouteImage
    {
        get { return routeImage; }
        set
        {
            routeImage = value;
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(nameof(RouteImage)));
        }
    }

    public void LoadImage(string archiveName, string entryName)
    {
        using (var archive = ZipFile.OpenRead(archiveName))
        {
            var entry = archive.GetEntry(entryName);
            if (entry != null)
            {
                using (var zipStream = entry.Open())
                using (var memoryStream = new MemoryStream())
                {
                    zipStream.CopyTo(memoryStream); // here
                    memoryStream.Position = 0;

                    var bitmap = new BitmapImage();
                    bitmap.BeginInit();
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;
                    bitmap.StreamSource = memoryStream;
                    bitmap.EndInit();

                    RouteImage = bitmap;
                }
            }
        }
    }
}

将视图模型的实例分配给Window的DataContext:

public MainWindow()
{
    InitializeComponent();

    var viewModel = new ViewModel();
    DataContext = viewModel;

    viewModel.LoadImage(
        @"D:\Games\steamapps\common\RailWorks\Content\Routes\00000036-0000-0000-0000-000000002012\MainContent.ap",
        "RouteInformation/image.png");
}

并绑定RouteImage属性,如下所示:

<Image Source="{Binding RouteImage}"/>

如果您打算从zip存档加载大图像文件,我建议使用异步方法调用视图模型代码:

public async Task LoadImageAsync(string archiveName, string entryName)
{
    RouteImage = await Task.Run(() => LoadImage(archiveName, entryName));
}

private BitmapImage LoadImage(string archiveName, string entryName)
{
    BitmapImage bitmap = null;

    using (var archive = ZipFile.OpenRead(archiveName))
    {
        var entry = archive.GetEntry(entryName);
        if (entry != null)
        {
            using (var zipStream = entry.Open())
            using (var memoryStream = new MemoryStream())
            {
                zipStream.CopyTo(memoryStream);
                memoryStream.Position = 0;

                bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.StreamSource = memoryStream;
                bitmap.EndInit();
                bitmap.Freeze(); // necessary when loaded in non-UI thread
            }
        }
    }

    return bitmap;
}

从MainWindow中的异步Loaded事件处理程序调用该方法:

Loaded += async (s, e) => await viewModel.LoadImageAsync
    @"D:\Games\steamapps\common\RailWorks\Content\Routes\00000036-0000-0000-0000-000000002012\MainContent.ap",
    "RouteInformation/image.png");

答案 1 :(得分:0)

在我从zip容器中删除图像的工作中,按照Clemens回答使用中间线程只会创建一个异常,而且难以成立。

最终,我能够使用此代码来提取BitmapImage

public static class StreamExtensions
{
    public static BitmapImage ToImage(this Stream stream)
    {       
        var bitmap = new BitmapImage();
        bitmap.BeginInit();
        bitmap.StreamSource = stream;
        bitmap.CacheOption = BitmapCacheOption.OnLoad;
        bitmap.EndInit();

        return bitmap;
     }
}

用法

var image = zipEntry.Open().ToImage();