天天看點

笨笨圖檔批量下載下傳器 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 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

          說明:界面上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.     接下來貼部分關鍵代碼和講解,這裡隻講異步的代碼,源碼注釋也比較多:

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new
笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

/// <summary>

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        /// 開始異步分析下載下傳

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        /// </summary>

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        /// <param name="url"></param>

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        /// <param name="savepath"></param>

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        private void asyncanalyzeanddownload(string url, string savepath)

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        {

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

            this.uristring = url;

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

            this.savepath = savepath;

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new
笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

            初始化全局變量

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new
笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

            分析計時開始

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new
笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

            using (webclient wclient = new webclient())

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

            {

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                autoresetevent waiter = new autoresetevent(false);

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                //為異步結果傳回傳參

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                wclient.querystring.add("url", uristring);

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                wclient.querystring.add("isincluecssimages", _isincluecssimages.tostring());

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                wclient.credentials = credentialcache.defaultcredentials;

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                wclient.downloaddatacompleted += new downloaddatacompletedeventhandler(asyncurianalyze);

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                wclient.downloaddataasync(new uri(uristring), waiter);

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

                //waiter.waitone();     //阻止目前線程,直到收到信号

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

            }

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        }

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

          說明:這裡是異步分析和下載下傳的入口,傳入網址、儲存路徑、是否分析在css檔案内的圖檔參數。

 ----------------------------------------------------------------------------------------------------------

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        /// 異步分析指定網址傳回的資料

        /// <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();

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

          說明:這部分代碼是程式的主體部分之一,如果需要分析css檔案内的圖檔,則采用遞歸調用本方法,analyzecontent方法為分析圖檔連結核心代碼,這裡發現比較有意思的是可以為下次接受的資料傳參,即自己傳給自己,這樣對于判斷是否分析完畢有很大便利性,代碼如:wclient.querystring.add("url", item);

------------------------------------------------------------------------------------------------------

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        /// 分析連結

        /// <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);

                        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);

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

          說明:這塊代碼是分析圖檔連結的代碼,由于正規表達式缺陷,加入了一些處理;加入了線程池控制,說到這裡,關于異步線程池的控制幾乎沒找到中文資料,就在google code seach裡面搜尋到了以下代碼片段:

iasyncresult res = hwr.begingetresponse(new asynccallback(asyncdownload), hwr);

          具體如何測試是否加入連接配接池也沒有深入的研究了,是以前面提到新加功能時後面打了一個問号,望資深人士幫忙分析下:)

-------------------------------------------------------------------------------------------------------------

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

        /// 異步接受資料

        /// <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);

            //從未下載下傳的清單中删除已經下載下傳的圖檔

                imgurllist.remove(url);

                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)

笨笨圖檔批量下載下傳器 V0.3 beta[C# | WinForm | 正規表達式 | HttpWebRequest | Async異步程式設計] new

          說明:這部分代碼是異步下載下傳的代碼,加入了人性化了多符号标示檔案的狀态。

          代碼注釋後可講解的就不多了,比較難的就是異步中同步資料控制,得控制好了并且盡量少,否則對性能能較大影響,大夥有興趣的話看代碼吧:)

結束

          感謝能閱讀到此處的網友,希望能多多交流關于異步、同步、正規表達式方面的經驗,不吝賜教!在下載下傳中可能出現其他意外情況,導緻圖檔已經下載下傳完畢但是下載下傳圖檔的按鈕不可用,狀态欄顯示正在下載下傳...,持續時間超過1分鐘,那麼可能是哪裡出了bug,呵呵!可以強制關閉然後重新啟動程式下載下傳,或者确認已經下載下傳完畢,狀态沒法恢複可以從工具->初始化來重置一下。

補充

     做這個小軟體隻有兩個目的:

          1.     學習交流

          2.     希望能幫得到大家

     希望和大家多多交流使用到的相關技術,以期能積累更多這方面的經驗,謝謝大家支援!!

轉載:http://www.cnblogs.com/over140/archive/2008/10/31/1322786.html