从 Zip 存档中读取 BitmapImage

Read BitmapImage from Zip archive

对于 WPF 项目,我需要从 Zip 文件中读取 BitmapImage。原始文件格式是 .png 我知道如何直接从文件系统执行此操作并且工作正常。来自 Zip 文件,遗憾的是图像未显示,但图像似乎已被读取。

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

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:\Temp\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 代码如下所示:

<Image Height="128" Width="256"  Source="{Binding BitmapImage}"/>

在调试器中,流似乎已创建并绑定到 BitmapImage,但宽度和高度设置为 1。所以我认为读取 zip 文件中的数据有问题。

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

您还应该编写一个带有 属性 更改通知的视图模型 class:

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[=11=]000036-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[=14=]000036-0000-0000-0000-000000002012\MainContent.ap",
    "RouteInformation/image.png");

在我从 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();