做了一個照片批量壓縮工具,其實核心代碼幾分鍾就完成了,但整個小工具做下來還是花了一天的時間。中間遇到了大堆問題,並尋求最好的解決方案予以解決。現在就分享一下這個看似簡單的小工具所使用的技術。
軟件界面如下:

要做真實場景的測試,拿的都是單反照的大相片:圖片尺寸3888*2592 圖片大小5.37M:
其中遇到的問題與解決方案分享:
1.用listview顯示圖片縮略圖非常慢的問題
這個問題是始料未及的,如果不做也可以,但是沒有縮略圖就有損軟件體驗,這是所有追求完美的程序員所不能容忍的,我當然也不例外。
最初的代碼如下:(此方法加載每張5M左右的圖片需要200-500ms)
listView1.Items.Clear();
imageList1.Images.Clear();
DirectoryInfo TheFolder = new DirectoryInfo(folderBrowserDialog1.SelectedPath);//文件路徑
List<string> ImgNames = new List<string>();
string allowImg = ".jpg.jpeg.png.bmp";
FileInfo[] Files = TheFolder.GetFiles();
for (int i = 0; i < Files.Length; i++)//遍歷文件夾
{
if (Files[i].Length > 0 &&allowImg.IndexOf(Files[i].Extension.ToLower())>-1)//或者jpg,png 文件大小要大於0且是圖片文件
{
Image image = Image.FromFile(Files[i].DirectoryName + "\\" + Files[i].Name); //獲取文件
ImgNames.Add(Files[i].Name);//添加文件名
imageList1.Images.Add(image);//添加圖片
}
}
//初始化設置
this.listView1.View = View.LargeIcon;
this.listView1.LargeImageList = this.imageList1;
//開始綁定
this.listView1.BeginUpdate();
for (int i = 0; i < ImgNames.Count; i++)
{
ListViewItem lvi = new ListViewItem();
lvi.ImageIndex = i;
lvi.Text = ImgNames[i];
this.listView1.Items.Add(lvi);
}
this.listView1.EndUpdate();
解決辦法是用微軟提供的Windows API Code Pack 1.0.1庫,通過該庫可以直接使用到win7/vista/win8系統的一些特性功能,如資源管理器、桌面、任務欄等等。詳細介紹見官方主頁
本程序使用WindowsApiCode完成對文件夾下的圖片迅速建縮略圖的代碼如下:
先在界面上添加一個該庫提供的explorerBrowser控件,然后初始化該控件:
//設置圖片展示控件屬性
explorerBrowser1.ContentOptions.ViewMode = ExplorerBrowserViewMode.List;
explorerBrowser1.NavigationOptions.PaneVisibility.Navigation = PaneVisibilityState.Hide;
explorerBrowser1.NavigationOptions.PaneVisibility.CommandsView = PaneVisibilityState.Hide;
explorerBrowser1.NavigationOptions.PaneVisibility.CommandsOrganize = PaneVisibilityState.Hide;
explorerBrowser1.NavigationOptions.PaneVisibility.Commands = PaneVisibilityState.Hide;
explorerBrowser1.SelectionChanged += new EventHandler(explorerBrowser1_SelectionChanged);
完成打開文件夾並顯示圖片縮略圖的代碼非常簡單:
//打開圖片文件夾
private void btnOpenDir_Click(object sender, EventArgs e)
{
// 創建打開文件夾對話框
CommonOpenFileDialog cfd = new CommonOpenFileDialog();
// 設置對話框屬性
cfd.IsFolderPicker = true;
cfd.AllowNonFileSystemItems = true;
// 彈出對話框並返回用戶的選擇
CommonFileDialogResult result = cfd.ShowDialog();
//如果用戶確定
if (result == CommonFileDialogResult.Ok)
{
// 獲取選擇對象的ShellObject形式
ShellObject resultItem = cfd.FileAsShellObject;
//用explorerBrowser控件顯示圖片列表
explorerBrowser1.Navigate(resultItem);
}
}
采用這種方法打開圖片縮略圖列表時間可以忽略不計。
2.好看的圖片界面庫
從前面的界面可以看出,本工具的界面並不丑,可以說還很精美,這也是花了心思的。
本工具的界面我采用的
官方主頁為http://www.componentfactory.com/
3.充分利用多核並行計算,提高圖片處理速度
處理批量任務當然要考慮速度,否則就失去了工具的意義了
.netFrameWork4.0里面提供了Parallel系列、Task系列來支持並行運算,讓並行計算變得如此簡單(為什么不跟着微軟走呢,后悔了吧 ^_^)。
並行指的是利用現在的CUP多核,同時開啟多個任務。跟以往的並發計算不同的是,並發的多個線程其實並非真正同時在運行,他們只是按照時間片,走走停停,邏輯上在同時進行,而並行則是在多個完全獨立的核上同時運行任務,是真正的同時在跑。
本程序中並行進行圖片壓縮的代碼如下:
ParallelOptions po = new ParallelOptions();
po.MaxDegreeOfParallelism = 15; //最多並發50個任務
//並行進行圖片壓縮
System.Threading.ThreadPool.QueueUserWorkItem(w=>{
Parallel.ForEach(imgtoComp, po, (o) =>
{
System.Drawing.Image sourceImg = System.Drawing.Image.FromFile(o.ParsingName);
int iWidth = 0;
int iHeight = 0;
if (rbtper.Checked)
{
int per = int.Parse(txtper.Text);
iWidth = sourceImg.Width * per / 100;
iHeight = sourceImg.Height * per / 100;
}
if (rbtheight.Checked)//最大高度
{
iHeight = int.Parse(txtheight.Text);
iWidth = iHeight * sourceImg.Width / sourceImg.Height;
}
if (rbtwidth.Checked)//最大寬度
{
iWidth = int.Parse(txtwidth.Text);
iHeight = iWidth * sourceImg.Height / sourceImg.Width;
}
System.Drawing.Image ThumbImg = ImgCompress.GetImageThumb(sourceImg, iWidth, iHeight);
if (rbtpng.Checked) ThumbImg.Save(FileSavePath + Path.GetFileNameWithoutExtension(o.Name) + ".png", System.Drawing.Imaging.ImageFormat.Png);
if (rbtgif.Checked) ThumbImg.Save(FileSavePath + Path.GetFileNameWithoutExtension(o.Name) + ".gif", System.Drawing.Imaging.ImageFormat.Gif);
if (rbtjpg.Checked) ThumbImg.Save(FileSavePath + Path.GetFileNameWithoutExtension(o.Name) + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
sourceImg.Dispose();
ThumbImg.Dispose();
Interlocked.Increment(ref ifinish);//ifinish++
this.Invoke(this.mysetFinish, new Object[] { ifinish }); //刷新進度條等
});
}, null);
這里主要強調一下並發任務數量的設置、以及資源的顯示釋放。
並發數量通過ParallelOptions參數的MaxDegreeOfParallelism來設置,這里必須設置,否則幾百張5M的圖片同時跑,立馬內存就占滿了。
資源的顯式釋放:sourceImg.Dispose(); ThumbImg.Dispose(); 這點也非常重要,處理大圖片是非常耗內存的,測試過程中就因為沒有顯式釋放內存,偷懶想着.net的自動垃圾回收機制會幫忙善后,結果跑到40多張圖片的時候就內存不足了。顯式處理資源釋放后,壓縮圖片的速度也因為空余的內存比較多而變快了。
下載本程序 Demo
