天天看点

笨笨图片批量下载器 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