笨笨图片批量下载器 V0.3 beta[C# | WinForm | 正则表达式 | HttpWebRequest | Async异步编程] new...
前言
?????從【笨笨圖片批量抓取下載 V0.2 beta】到【笨笨圖片批量下載器 V0.3 beta】時間將近2個月,不是說這個升級版本開發了這么久,實在是懶,呵呵: )再加有時候工作忙、學習,多的時間就不愿意動了,現在都感覺辜負了上一版N多朋友的支持了,不過這將近一個星期時間我按計劃完成了這個小軟件版的升級開發,并且依然和上兩個版本一樣保持源代碼開源,文章最后有下載地址,以下是這個版本相比上個版本的特點:
?????1.?????加入圖片是否重命名。
?????2.?????加入異步線程池控制(? 隨后解釋)。
?????3.?????加入圖片大小限制。
?????4.?????加入支持指定網址內CSS文件內的圖片下載。
?????5.?????加入了正則表達式即時配置更改(應變正則表達式缺陷)。
?????6.?????優化部分代碼。
?????7.?????修改部分統計錯誤。
?
感謝
?????1.?????感謝熱心回帖并提供建議的部分網友:Stoneq、liyundong、尋夢E.net、Caspar Jiong 、laoda、lexus 等
?????2.?????感謝Google Code?Seach,在我找不到任何我能看懂的中英文資料時(尤其是異步線程池控制),她提供了我參考代碼!!
?????3.?????感謝女朋友在精神上的莫大鼓舞!
?
正文
?????1.?????和以往一樣,先來一張圖,然后看圖說話:)
?????????????
??????????說明:界面上V0.3與V0.2差不多,剔除了圖片類型復選框,如果你需要下載指定類型的圖片的話,可以從 配置->設置正則表達式圖片鏈接分析 里面直接修改匹配圖片的正則表達式,最末端就是圖片的文件類型了,如:(jpg|jpeg|png|ico|bmp|gif);狀態欄增加了一個實際下載圖片數量,可以實時的顯示當前下載圖片的張數;多了一個[重命名]和[下載頁面包含CSS文件內的圖片],后者就不用說明了,但是前者需要說明一下:這個地方是費了我不少時間了,我原先的重命名方案是DateTime.Now.ToString("yyyyMMddHHmmssfff"),后來又加上了new Random().Next(100)和lock,都不對,顯示下載圖片的數量和文件夾里面的圖片數量不符合,并且選擇重命名和不選擇重命名文件夾實際圖片數相差較大,幾張到幾十張不等,最后改用了System.Guid.NewGuid()至此基本正確符合,所有猜想,DateTime.Now和Random在遇到異步多線程應該會出現臟讀吧?!
??????????欄目里面的配置,開始想的時候覺得有那么點畫蛇添足的味道,但是覺得有促進和各位朋友正則表達式交流的作用而保留下來了,所以期待牛人給予友情提示:)
?
?????2.?????接下來貼部分關鍵代碼和講解,這里只講異步的代碼,源碼注釋也比較多:
///?<summary>????????///?開始異步分析下載
????????///?</summary>
????????///?<param?name="url"></param>
????????///?<param?name="savePath"></param>
????????private?void?AsyncAnalyzeAndDownload(string?url,?string?savePath)
????????{
????????????this.uriString?=?url;
????????????this.savePath?=?savePath;
????????????初始化全局變量
????????????分析計時開始
????????????using?(WebClient?wClient?=?new?WebClient())
????????????{
????????????????AutoResetEvent?waiter?=?new?AutoResetEvent(false);
????????????????//為異步結果返回傳參
????????????????wClient.QueryString.Add("url",?uriString);
????????????????wClient.QueryString.Add("IsInclueCssImages",?_IsInclueCssImages.ToString());
????????????????wClient.Credentials?=?CredentialCache.DefaultCredentials;
????????????????wClient.DownloadDataCompleted?+=?new?DownloadDataCompletedEventHandler(AsyncURIAnalyze);
????????????????wClient.DownloadDataAsync(new?Uri(uriString),?waiter);
????????????????//waiter.WaitOne();?????//阻止當前線程,直到收到信號
????????????}
????????}
??????????說明:這里是異步分析和下載的入口,傳入網址、保存路徑、是否分析在CSS文件內的圖片參數。
?----------------------------------------------------------------------------------------------------------
///?<summary>????????///?異步分析指定網址返回的數據
????????///?</summary>
????????///?<param?name="sender"></param>
????????///?<param?name="e"></param>
????????protected?void?AsyncURIAnalyze(Object?sender,?DownloadDataCompletedEventArgs?e)
????????{
????????????AutoResetEvent?waiter?=?(AutoResetEvent)e.UserState;
????????????WebClient?nWC?=?sender?as?WebClient;
????????????bool?IsMatchCss?=?true;
????????????bool?IsInclueCssImages?=?Convert.ToBoolean(nWC.QueryString["IsInclueCssImages"]);
????????????try
????????????{
????????????????if?(!e.Cancelled?&&?e.Error?==?null)
????????????????{
????????????????????string?dnDir?=?string.Empty;
????????????????????string?domainName?=?string.Empty;
????????????????????string?uri?=?nWC.QueryString["url"];
????????????????????if?(!uri.StartsWith("http://")?&&?!uri.StartsWith("https://"))
????????????????????????uri?=?string.Concat("http://",?uri);
????????????????????//獲得域名?http://www.sina.com
????????????????????domainName?=?GetDomain(uri);
????????????????????//獲得域名最深層目錄?http://www.sina.com/mail/
????????????????????dnDir?=?GetFullPath(domainName,?uri);
????????????????????//獲取數據
????????????????????string?pageData?=?Encoding.UTF8.GetString(e.Result);
????????????????????//匹配全路徑
????????????????????AnalyzeContent(Regex.Matches(pageData,?ImagePattern),?domainName,?dnDir);
????????????????????//是否下載頁面包含CSS文件內的圖片
????????????????????if?(IsInclueCssImages)
????????????????????{
????????????????????????//匹配CSS文件?//[\w=/]*((\.css){1})
????????????????????????MatchCollection?mc?=?Regex.Matches(pageData,?CssPattern,?RegexOptions.IgnoreCase);
????????????????????????for?(int?i?=?0,?j?=?mc.Count;?i?<?j;?i++)
????????????????????????{
????????????????????????????string?item?=?mc[i].Value;
????????????????????????????//短路徑處理
????????????????????????????if?(!item.StartsWith("http://")?&&?!item.StartsWith("https://"))
????????????????????????????????item?=?(item[0]?==?'/'???domainName?:?dnDir)?+?item;
????????????????????????????using?(WebClient?wClient?=?new?WebClient())
????????????????????????????{
????????????????????????????????AutoResetEvent?are?=?new?AutoResetEvent(false);
????????????????????????????????wClient.QueryString.Add("url",?item);
????????????????????????????????wClient.QueryString.Add("IsOver",?i?==?j?-?1???"1"?:?"0");
????????????????????????????????wClient.QueryString.Add("IsInclueCssImages",?"false");
????????????????????????????????wClient.Credentials?=?CredentialCache.DefaultCredentials;
????????????????????????????????wClient.DownloadDataCompleted?+=?new?DownloadDataCompletedEventHandler(AsyncURIAnalyze);
????????????????????????????????wClient.DownloadDataAsync(new?Uri(item),?are);
????????????????????????????}
????????????????????????}
????????????????????????if?(mc.Count?==?0)
????????????????????????????IsMatchCss?=?false;
????????????????????}
????????????????}
????????????}
????????????finally
????????????{
????????????????//waiter.Set();
????????????????if?((IsInclueCssImages?&&?!IsMatchCss)?||?(!IsInclueCssImages?&&?string.IsNullOrEmpty(nWC.QueryString["IsOver"]))?||?(!string.IsNullOrEmpty(nWC.QueryString["IsOver"])?&&?"1"?==?nWC.QueryString["IsOver"]))
????????????????{
????????????????????lock?(slock)
????????????????????{
????????????????????????#region?分析計時結束
????????????????????????QueryPerformanceCounter(ref?count1);
????????????????????????count?=?count1?-?count;
????????????????????????
????????????????????????toolStripStatusLabel1.Text?=?"分析完畢!";
????????????????????????toolStripStatusLabel2.Text?=?string.Format("?|?分析耗時:{0:#.####}秒",?(double)(count)?/?(double)freq);
????????????????????????#endregion
????????????????????????//分析完畢
????????????????????????isAnalyzeComplete?=?true;
????????????????????}
????????????????????Application.DoEvents();
????????????????}
????????????}
????????}
??????????說明:這部分代碼是程序的主體部分之一,如果需要分析CSS文件內的圖片,則采用遞歸調用本方法,AnalyzeContent方法為分析圖片鏈接核心代碼,這里發現比較有意思的是可以為下次接受的數據傳參,即自己傳給自己,這樣對于判斷是否分析完畢有很大便利性,代碼如:wClient.QueryString.Add("url", item);
------------------------------------------------------------------------------------------------------
///?<summary>????????///?分析鏈接
????????///?</summary>
????????///?<param?name="mc"></param>
????????///?<param?name="domainName"></param>
????????///?<param?name="dnDir"></param>
????????private?void?AnalyzeContent(MatchCollection?mc,?string?domainName,?string?dnDir)
????????{
????????????List<string>?urlList?=?new?List<string>();
????????????foreach?(Match?mt?in?mc)
????????????{
????????????????string?item?=?mt.Value;
????????????????/*??處理圖片正則表達式的缺陷,即圖片必須帶域名,如:
?????????????????*??????當前正則表達式匹配http://www.icoxxx.com
?????????????????*??????匹配結果為http://www.ico
?????????????????*/
????????????????if?(item.Length?>?8?&&?item.IndexOf('/',?9)?==?-1)
????????????????????continue;
????????????????//短路徑處理
????????????????if?(!item.StartsWith("http://")?&&?!item.StartsWith("https://"))
????????????????????item?=?(item[0]?==?'/'???domainName?:?dnDir)?+?item;
????????????????//處理../
????????????????if?(item.IndexOf("../")?!=?-1)
????????????????{
????????????????????List<string>?urls?=?new?List<string>();
????????????????????urls.AddRange(item.Split('/'));
????????????????????for?(int?i?=?0;?i?<?urls.Count;?i++)
????????????????????????if?("..".Equals(urls[i]))
????????????????????????{
????????????????????????????urls.RemoveRange(i?-?1,?2);
????????????????????????????i?-=?2;
????????????????????????}
????????????????????item?=?Join("/",?urls);
????????????????}
????????????????if?(!urlList.Contains(item))
????????????????{
????????????????????urlList.Add(item);
????????????????????lock?(slock)
????????????????????{
????????????????????????imgUrlList.Add(item);
????????????????????}
????????????????????//實時顯示分析結果
????????????????????AddlbShowItem(item);
????????????????????//邊分析邊下載
????????????????????HttpWebRequest?hwr?=?WebRequest.Create(item)?as?HttpWebRequest;
????????????????????hwr.AllowWriteStreamBuffering?=?false;
????????????????????//hwr.ReadWriteTimeout?=?5?*?1000;?//默認超時30秒
????????????????????IAsyncResult?res?=?hwr.BeginGetResponse(new?AsyncCallback(AsyncDownLoad),?hwr);
????????????????????//加入線程池控制
????????????????????ThreadPool.RegisterWaitForSingleObject(res.AsyncWaitHandle,?new?WaitOrTimerCallback(timeoutCallback),?hwr,?Timeout,?true);
????????????????}
????????????}
????????}
??????????說明:這塊代碼是分析圖片鏈接的代碼,由于正則表達式缺陷,加入了一些處理;加入了線程池控制,說到這里,關于異步線程池的控制幾乎沒找到中文資料,就在Google Code Seach里面搜索到了以下代碼片段:
IAsyncResult?res?=?hwr.BeginGetResponse(new?AsyncCallback(AsyncDownLoad),?hwr);????????????????????//加入線程池控制
????????????????????ThreadPool.RegisterWaitForSingleObject(res.AsyncWaitHandle,?new?WaitOrTimerCallback(timeoutCallback),?hwr,?Timeout,?true);
??????????具體如何測試是否加入連接池也沒有深入的研究了,所以前面提到新加功能時后面打了一個問號,望資深人士幫忙分析下:)
-------------------------------------------------------------------------------------------------------------
///?<summary>????????///?異步接受數據
????????///?</summary>
????????///?<param?name="asyncResult"></param>
????????public?void?AsyncDownLoad(IAsyncResult?asyncResult)
????????{
????????????#region?下載計時開始
????????????lock?(slock)
????????????{
????????????????if?(cfreq?==?0L)
????????????????{
????????????????????ccount?=?0L;?
????????????????????QueryPerformanceFrequency(ref?cfreq);
????????????????????QueryPerformanceCounter(ref?ccount);
????????????????}
????????????}
????????????#endregion
????????????
????????????WebRequest?request?=?(WebRequest)asyncResult.AsyncState;
????????????string?url?=?request.RequestUri.ToString();
????????????int?indexItem?=?this.lbShow.Items.IndexOf(url);
????????????//從未下載的列表中刪除已經下載的圖片
????????????lock?(slock)
????????????{
????????????????imgUrlList.Remove(url);
????????????}
????????????
????????????try
????????????{
????????????????WebResponse?response?=?request.EndGetResponse(asyncResult);
????????????????long?cLength?=?response.ContentLength;
????????????????if?(cLength?>?0?&&?cLength?<=?FileSize)
????????????????{
????????????????????using?(Stream?stream?=?response.GetResponseStream())
????????????????????{
????????????????????????Image?img?=?Image.FromStream(stream);
????????????????????????img.Save(string.Concat(savePath,?"/",?GetFileName(url)));
????????????????????????img.Dispose();
????????????????????????stream.Close();
????????????????????}
????????????????????//allDone.Set();
????????????????????//成功下載
????????????????????if?(indexItem?>=?0?&&?indexItem?<=?this.lbShow.Items.Count)
????????????????????????SetlbShowItem(indexItem,?"√??");
????????????????}
????????????????else?if?(cLength?==?0L?&&?indexItem?>=?0)
????????????????????SetlbShowItem(indexItem,?"×??");
????????????????else?if?(indexItem?>=?0)
????????????????????SetlbShowItem(indexItem,?"!??");????//表示圖片過大或過小
????????????}
????????????catch?(Exception)
????????????{
????????????????if?(indexItem?>=?0)
????????????????????SetlbShowItem(indexItem,?"×??");
????????????}
????????}
??????????說明:這部分代碼是異步下載的代碼,加入了人性化了多符號標示文件的狀態。
??????????代碼注釋后可講解的就不多了,比較難的就是異步中同步數據控制,得控制好了并且盡量少,否則對性能能較大影響,大伙有興趣的話看代碼吧:)
?
結束
??????????感謝能閱讀到此處的網友,希望能多多交流關于異步、同步、正則表達式方面的經驗,不吝賜教!在下載中可能出現其他意外情況,導致圖片已經下載完畢但是下載圖片的按鈕不可用,狀態欄顯示正在下載...,持續時間超過1分鐘,那么可能是哪里出了BUG,呵呵!可以強制關閉然后重新啟動程序下載,或者確認已經下載完畢,狀態沒法恢復可以從工具->初始化來重置一下。
?
補充
?????感謝?Jon.Hong?的批評,確實下載回來執行有問題,原因是因為寫文章的時候臨時改了幾個參數忘了編譯測試了,已經更新了下載文件,不過查看源代碼直接調試也行!
?????做這個小軟件只有兩個目的:
??????????1.?????學習交流
??????????2.?????希望能幫得到大家
?????希望和大家多多交流使用到的相關技術,以期能積累更多這方面的經驗,謝謝大家支持!!
轉載:http://www.cnblogs.com/over140/archive/2008/10/31/1322786.html
總結
以上是生活随笔為你收集整理的笨笨图片批量下载器 V0.3 beta[C# | WinForm | 正则表达式 | HttpWebRequest | Async异步编程] new...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何将扫描的A4纸张图片转换为PDF并压
- 下一篇: Windows NT启动过程详解