对于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文件中的数据有些不对。
答案 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();