来自 Stream returns 1x1px 的 BitmapImage 而不是整个图像

BitmapImage from Stream returns 1x1px instead of the whole image

我有一个方法可以打开 FileStream 并创建 BitmapImage,方法是使用 StreamSource 属性.

不知何故,在一台机器上,尝试打开大图像 (6000x4000px) 导致该方法返回 1x1px 图像。

起初我以为图片是从本地网络上的共享文件夹加载的,但它存储在同一台计算机的下载文件夹中。

我看到图片是 "blocked/locked" by Windows 因为它是从未经验证的来源下载的,所以我打开属性并解锁了它。再次尝试加载图片出现同样的问题。

图像已完全下载。

背景资料:

出现问题的机器:

我的机器(按预期工作):

代码:

public static BitmapSource SourceFrom(this string fileSource, int? size = null)
{
    using (var stream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
    {
        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;

        if (size.HasValue)
        {
            //It's not possible to get the size of image while opening, so get from another place.
            var refSize = fileSource.ScaledSize(); //Gets the size of the image.

            if (refSize.Height > refSize.Width)
                bitmapImage.DecodePixelHeight = size.Value;
            else
                bitmapImage.DecodePixelWidth = size.Value;
        }

        bitmapImage.StreamSource = stream;               
        bitmapImage.EndInit();
        bitmapImage.Freeze(); //Just in case you want to load the image in another thread.
        return bitmapImage;
    }
}

用法:

var image = "C:\Image.jpg".SourceFrom(); //Without any other parameter.

问题:

可能的答案:

工作代码:

using (var stream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
    using (var memory = new MemoryStream())
    {
        stream.CopyTo(memory);
        memory.Position = 0;
//...

只需替换这部分代码,并在设置 StreamSource 对象时使用 memory 变量而不是 stream

好像是当图片文件很大,或者由于某些其他原因不能立即读取源流时,你必须将源流复制到一个中间的MemoryStream,并将其分配给StreamSource 属性 BitmapImage:

using (var fileStream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
using (var memoryStream = new MemoryStream())
{
    fileStream.CopyTo(memoryStream);
    memoryStream.Position = 0;

    var bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.CacheOption = BitmapCacheOption.OnLoad
    bitmapImage.StreamSource = memoryStream;               
    bitmapImage.EndInit();
    bitmapImage.Freeze();
    return bitmapImage;
}

我遇到了同样的问题,CacheOption 不在您代码中的正确位置!!只需在 endInit() 之前添加它;

 source.CacheOption = BitmapCacheOption.OnLoad;

像这样 ->

                BitmapImage source = new BitmapImage();
                source.BeginInit();
                source.CacheOption = BitmapCacheOption.OnLoad;
                source.StreamSource = fs;                  
                source.EndInit();

当图像解码失败时,BitmapImage 会创建一个 1x1px 的默认图像。您需要注册 DecodeFailed 事件才能检测到这一点。

Exception decodeEx = null;
using (var fileStream = new FileStream(fileSource, FileMode.Open, FileAccess.Read))
{
    var bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
    bitmapImage.StreamSource = fileStream;
    bitmapImage.DecodeFailed += (_, e) => decodeEx = e.ErrorException;
    bitmapImage.EndInit();

    if (decodeEx != null)
        throw decodeEx;

    bitmapImage.Freeze();
    return bitmapImage;
}

在我的例子中,结果是 OutOfMemoryException。事实上,只有当内存使用率很高并且 BitmapImage(使用非托管内存)实际调用的本机函数可能无法分配足够的内存时,解码才会失败。