做了一個照片批量壓縮工具,其實核心代碼幾分鍾就完成了,但整個小工具做下來還是花了一天的時間。中間遇到了大堆問題,並尋求最好的解決方案予以解決。現在就分享一下這個看似簡單的小工具所使用的技術。
軟件界面如下:
要做真實場景的測試,拿的都是單反照的大相片:圖片尺寸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