最近做一個小app遇到一個問題,到目前還沒有比較好的解決方法(可能是我查的資料不夠多)
需求如下:
1.把一個Image中的圖像保存到字節數組;
2.把字節數組轉換為ImageSource,通過Image控件展示圖像.
上面兩個需求恰恰是相反的過程,為了實現這個,我倒網上找了好多,但基本都是wp7,wp8,wpf的方案,在win10上沒法用。。糾結。
后來在知乎日報uwp的源碼中發現了一個把ImageSource存儲為文件的方法。(github:https://github.com/sherlockchou86/ZhiHuDaily.UWP),感謝作者為win10生態圈的貢獻。
代碼如下:

public async Task SaveImageAsync(WriteableBitmap image, string filename) { try { if (image == null) { return; } Guid BitmapEncoderGuid = BitmapEncoder.JpegEncoderId; if(filename.EndsWith("jpg")) BitmapEncoderGuid = BitmapEncoder.JpegEncoderId; else if(filename.EndsWith("png")) BitmapEncoderGuid = BitmapEncoder.PngEncoderId; else if(filename.EndsWith("bmp")) BitmapEncoderGuid = BitmapEncoder.BmpEncoderId; else if(filename.EndsWith("tiff")) BitmapEncoderGuid = BitmapEncoder.TiffEncoderId; else if(filename.EndsWith("gif")) BitmapEncoderGuid = BitmapEncoder.GifEncoderId; var folder = await _local_folder.CreateFolderAsync("images_cache", CreationCollisionOption.OpenIfExists); var file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting); using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite)) { BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoderGuid, stream); Stream pixelStream = image.PixelBuffer.AsStream(); byte[] pixels = new byte[pixelStream.Length]; await pixelStream.ReadAsync(pixels, 0, pixels.Length); encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)image.PixelWidth, (uint)image.PixelHeight, 96.0, 96.0, pixels); await encoder.FlushAsync(); } } catch { } }
有了這個,我的需求就能解決了,因為在上面的代碼中,有一個隨機訪問流ras,所以我們可以把ras利用拓展方法AsStream()轉化為普通的流,然后用Read()或者ReadAsync()讀取到字節數組即可。
於是需求一的代碼如下:
public static async Task<byte[]> SaveToBytesAsync(this ImageSource imageSource) { byte[] imageBuffer; var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder; var file = await localFolder.CreateFileAsync("temp.jpg", CreationCollisionOption.ReplaceExisting); using (var ras = await file.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None)) { WriteableBitmap bitmap = imageSource as WriteableBitmap; var stream = bitmap.PixelBuffer.AsStream(); byte[] buffer = new byte[stream.Length]; await stream.ReadAsync(buffer, 0, buffer.Length); BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, ras); encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight, 96.0, 96.0, buffer); await encoder.FlushAsync(); var imageStream = ras.AsStream(); imageStream.Seek(0, SeekOrigin.Begin); imageBuffer = new byte[imageStream.Length]; var re = await imageStream.ReadAsync(imageBuffer, 0, imageBuffer.Length); } await file.DeleteAsync(StorageDeleteOption.Default); return imageBuffer; }
值得注意的幾點:
1.用了拓展方法;
2.ImageSource一般可以通過BitmapImage和WriteableImage來設置,但這兒只能用WriteableImage,因為要用到接口IWriteableBitmap;
3.ras隨機訪問流轉為普通FCL普通流后,用Seek將Positon設置到0,讀出的數據出錯;
4.注意圖像編碼的設置,這兒只針對jpeg格式;
5.這是一個投機的辦法,先存儲為文件,再存儲為字節數組,但核心還是一個ras(IRandomAccessSteam).
需求二代碼如下:
public static async Task<ImageSource> SaveToImageSource(this byte[] imageBuffer) { ImageSource imageSource = null; using (MemoryStream stream = new MemoryStream(imageBuffer)) { var ras = stream.AsRandomAccessStream(); BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, ras); var provider = await decoder.GetPixelDataAsync(); byte[] buffer = provider.DetachPixelData(); WriteableBitmap bitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight); await bitmap.PixelBuffer.AsStream().WriteAsync(buffer, 0, buffer.Length); imageSource = bitmap; } return imageSource; }
注意點如下:
1.解碼用的還是jpeg;
2.還是用的WriteableBitmap,主要還是他的接口決定的;
tips:如果照的上面的代碼寫,某些方法或屬性下面還是有紅色波浪線的話,記着選中它,然后按Shift+alt+F10,Visual Studio幫你解決。