作了一個照片批量壓縮工具,其實核心代碼幾分鐘就完成了,但整個小工具作下來仍是花了一天的時間。中間遇到了大堆問題,並尋求最好的解決方案予以解決。如今就分享一下這個看似簡單的小工具所使用的技術。程序員
軟件界面以下:web
要作真實場景的測試,拿的都是單反照的大相片:圖片尺寸3888*2592 圖片大小5.37M:併發
其中遇到的問題與解決方案分享:ide
1.用listview顯示圖片縮略圖很是慢的問題工具
這個問題是始料未及的,若是不作也能夠,可是沒有縮略圖就有損軟件體驗,這是全部追求完美的程序員所不能容忍的,我固然也不例外。測試
最初的代碼以下:(此方法加載每張5M左右的圖片須要200-500ms)this
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系統的一些特性功能,如資源管理器、桌面、任務欄等等。詳細介紹見官方主頁 spa
本程序使用WindowsApiCode完成對文件夾下的圖片迅速建縮略圖的代碼以下:.net
先在界面上添加一個該庫提供的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