2012年8月18日 星期六

使用 WriteableBitmap.SaveJpeg 需注意壓縮問題

事情的經過是這樣子的,我們有時後會想要從網路上抓一些圖片下來,
並且寫了一支程式定期去檢查是不是換了新的圖片,若已更換圖片,就把新圖也存成檔案,
是不是換了新的圖最簡單的識別方法就是比較已存檔的最新圖片與網路圖片的 MD5

以下是我寫的程式碼片段,流程很簡單,
我利用 WebClient 將圖片取下來,塞到 BitmapImage 看一下,
然後立即用 WriteableBitmap 將這張 BitmapImage 存成檔案。

Stream imageStream = e.Result; // 上略,總之就是利用 WebClient 取得圖片 Stream
BitmapImage bmp = new BitmapImage();
bmp.SetSource(imageStream); // 此時 bmp 可以放到 Image Component 看看長怎樣
if (bmp != null)
{
    String strPath = "NewImage.jpg";
    IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
    IsolatedStorageFileStream file = isoStore.CreateFile(strPath);

    WriteableBitmap wb = new WriteableBitmap(bmp);
    wb.SaveJpeg(file, wb.PixelWidth, wb.PixelHeight, 0, 100);

    file.Close();
    file.Dispose();
    isoStore.Dispose();
}
imageStream.Close();
imageStream.Dispose();

奇怪,為什麼網路上的圖片沒更新,我寫的抓圖工具還是一直把圖取下來呢?
查了一下發現網路上直接取下來的 Stream 所編出來的 MD5 與檔案不一樣,
直覺上就是圖片被存成檔案時遭到壓縮所造成的內容修改,
但即便我在 WriteableBitmap 的 quality 參數已設為 100 還是會發生,
解決方法其實很簡單,因為來源就是 Jpeg 所以直接以 Bytes 的方式寫成檔案就好了,
以下是我後來存檔的方式,這時檔案與網路圖片的 MD5 當然就對得上嘍。

Stream imageStream = e.Result; // 上略,總之就是利用 WebClient 取得圖片 Stream
BitmapImage bmp = new BitmapImage();
bmp.SetSource(imageStream); // 此時 bmp 可以放到 Image Component 看看長怎樣
if (bmp != null)
{
    String strPath = "NewImage.jpg";
    IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
    IsolatedStorageFileStream file = isoStore.CreateFile(strPath);

    Byte[] btImage = new Byte[imageStream.Length];
    int nRead = imageStream.Read(btImage, 0, (int)imageStream.Length);
    if (nRead == imageStream.Length)
    {
        file.Write(btImage, 0, nRead);
    }

    file.Close();
    file.Dispose();
    isoStore.Dispose();
}
imageStream.Close();
imageStream.Dispose();

對了,BitmapImage 及 WriteableBitmap 使用時必須在 UI Thread,
若沒有立即查看圖片的需求,或這些事需要寫在 Background Thread 中,
直接以 Bytes 的方式儲存 Stream 正好也可以避開 Thread 的問題。

沒有留言:

張貼留言