【问题标题】:How can I save an image from an ImageSource without downloading again?如何从 ImageSource 保存图像而无需再次下载?
【发布时间】:2012-06-20 08:09:33
【问题描述】:

这是我之前的问题how should a metro app cache images for tombstoning (and should it)?的扩展

我一直在寻找的解决方案是使用 HttpClient,但这会导致 Web 服务器上的每个文件不必要的第二次命中。

是否可以从 Image/ImageSource 保存现有的图像/流(使用 ImageOpened 事件来确保它可用)?

解决方案必须与当前的 RP API 配合使用,因为这是 CP 进行了重大更改的领域。

【问题讨论】:

    标签: xaml windows-8 microsoft-metro


    【解决方案1】:

    首先请注意,如果没有做任何自定义(即使用 URI 作为 Image.Source),已经实现了某种缓存,就好像您在应用旁边运行了 Fiddler,您会看到请求只发出一次,而不是每次显示项目时发出。

    话虽如此,如果您想要某种持久缓存,您可以创建一个转换器来检查所需的图像是否在应用程序的临时目录中,如果不是,则下载它。

    这是一种方法:

    public sealed class UriToCachedImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            Uri uri = value as Uri;
            if (uri == null)
                return null;
    
            // Don't let user know those are images, yet keep a unique and consistent name.
            string fileName = Path.GetFileName(uri.AbsolutePath);
            StorageFile file = null;
            try
            {
                Task<StorageFile> task = ApplicationData.Current.TemporaryFolder.GetFileAsync(fileName).AsTask<StorageFile>();
                task.Wait();
                file = task.Result;
            }
            catch (AggregateException ex)
            {
                if (ex.InnerException is FileNotFoundException)
                {
                    // http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/1eb71a80-c59c-4146-aeb6-fefd69f4b4bb/
                }
                else
                {
                    throw;
                }
            }
    
            if (file == null)
            {
                // File isn't present in cache => download it.
    
                // This creates a file in the app's INetCache
                // but we can't retrieve its path or know its name for sure
                // hence we're not using this file as our cache file.
                Task<StorageFile> streamTask = StorageFile.CreateStreamedFileFromUriAsync(fileName, uri, RandomAccessStreamReference.CreateFromUri(uri)).AsTask<StorageFile>();
                streamTask.Wait();
                StorageFile storageFile = streamTask.Result;
    
                // Copy file to our temporary directory (can't move as we don't have write access on the initial file).
                Task<StorageFile> copyTask = storageFile.CopyAsync(ApplicationData.Current.TemporaryFolder, fileName, NameCollisionOption.ReplaceExisting).AsTask<StorageFile>();
                copyTask.Wait();
                file = copyTask.Result;
            }
    
            Task<IRandomAccessStreamWithContentType> openTask = file.OpenReadAsync().AsTask<IRandomAccessStreamWithContentType>();
            openTask.Wait();
            IRandomAccessStreamWithContentType randomAccessStream = openTask.Result;
    
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.SetSource(randomAccessStream);
            return bitmapImage;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
    

    注意:这段代码 sn-p 还说明了如何从同步方法中调用异步方法;)

    【讨论】:

    • 感谢您发布此消息!对我来说,CreateStreamdFileFromUriAsync 检索文件,但是当我尝试从该流文件执行 CopyAsync 到存储文件以实际缓存它时,我得到异常“系统找不到指定的文件。(来自 HRESULT 的异常:0x80070002)”。我在这里错过了一步吗?
    【解决方案2】:

    我无法从 ImageOpened 事件中看到我需要的方法,但我找到了一个工作模型。

    不是将图像uri绑定到图像源并让metro下载图像,而是绑定到图像的标签,然后在数据绑定完成后循环遍历xaml中的每个图像并将图像下载为RandomAccessStream,保存流并将 Image Source 设置为从流中创建的 BitmapImage。

    RandomAccessStreamReference rasf = RandomAccessStreamReference.CreateFromUri(new Uri(MyImage.Tag as string));
    BitmapImage bitmapImage = new BitmapImage();
    var ras = await rasf.OpenReadAsync();
    bitmapImage.SetSource(ras);
    MyImage.Source = bitmapImage;
    

    我还研究了通过将代码放在转换器和视图模型属性中来实现流程自动化,但由于异步调用,这两者都不可能。

    【讨论】:

      【解决方案3】:

      您还可以使用 FFImageLoading (https://github.com/molinch/FFImageLoading/) 或查看其源代码以了解其实现方式 (https://github.com/molinch/FFImageLoading/tree/master/FFImageLoading.Windows)

      特点

      • Xamarin.iOS(最低 iOS 7)、Xamarin.Android(最低 Android 4)、Xamarin.Forms 和 Windows(WinRT、UWP)支持
      • 可配置的磁盘和内存缓存
      • 类似下载/加载请求的重复数据删除
      • 错误和加载占位符支持
      • 图像可以自动下采样到指定大小(减少内存使用)
      • WebP 支持
      • 图像加载淡入动画支持
      • 可以重试图片下载(RetryCount、RetryDelay)
      • 默认情况下禁用 Android 透明度(可配置)。节省 50% 的内存
      • 转换支持
        • 模糊转换
        • CircleTransformation、RoundedTransformation、CornersTransformation
        • ColorSpaceTransformation、GrayscaleTransformation、SepiaTransformation
        • 翻转变换
        • 支持自定义转换(本机平台 ITransformation 实现)

      就这么简单:

      <ff:FFImage Name="image"
          VerticalAlignment="Stretch" 
          HorizontalAlignment="Stretch"
          LoadingPlaceholder="loading.png"
          ErrorPlaceholder="error.png"
          CacheDuration="30"
          RetryCount="3"
          RetryDelay="250"
          DownsampleHeight="300"
          Source="http://lorempixel.com/output/city-q-c-600-600-5.jpg">
      </ff:FFImage>
      

      此处的示例项目:https://github.com/molinch/FFImageLoading/tree/master/samples/Simple.WinPhone.Sample

      【讨论】:

        猜你喜欢
        • 2015-08-06
        • 2016-03-27
        • 2019-12-09
        • 2011-02-03
        • 1970-01-01
        • 2014-09-10
        • 1970-01-01
        • 2011-05-07
        • 1970-01-01
        相关资源
        最近更新 更多