笨笨图片批量下载器 V0.3 beta[C# | WinForm | 正则表达式 | HttpWebRequest | Async异步编程] new

简介:

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.NowRandom在遇到异步多线程应该会出现脏读吧?!

          栏目里面的配置,开始想的时候觉得有那么点画蛇添足的味道,但是觉得有促进和各位朋友正则表达式交流的作用而保留下来了,所以期待牛人给予友情提示:)

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,呵呵!可以强制关闭然后重新启动程序下载,或者确认已经下载完毕,状态没法恢复可以从工具->初始化来重置一下。

本文转自博客园农民伯伯的博客,原文链接:笨笨图片批量下载器 V0.3 beta[C# | WinForm | 正则表达式 | HttpWebRequest | Async异步编程] new,如需转载请自行联系原博主。

目录
相关文章
|
4月前
|
数据采集 JavaScript C#
C#图像爬虫实战:从Walmart网站下载图片
C#图像爬虫实战:从Walmart网站下载图片
|
9天前
|
Linux C# iOS开发
开源GTKSystem.Windows.Forms框架让C# Winform支持跨平台运行
开源GTKSystem.Windows.Forms框架让C# Winform支持跨平台运行
43 12
|
9天前
|
C#
C#正则表达式快速入门
C#正则表达式快速入门
|
2月前
|
C# UED SEO
C# 异步方法async / await任务超时处理
通过使用 `Task.WhenAny`和 `Task.Delay`方法,您可以在C#中有效地实现异步任务的超时处理机制。这种方法允许您在指定时间内等待任务完成,并在任务超时时采取适当的措施,如抛出异常或执行备用操作。希望本文提供的详细解释和代码示例能帮助您在实际项目中更好地处理异步任务超时问题,提升应用程序的可靠性和用户体验。
81 3
|
4月前
|
SQL API 定位技术
基于C#使用winform技术的游戏平台的实现【C#课程设计】
本文介绍了基于C#使用WinForms技术开发的游戏平台项目,包括项目结构、运行截图、实现功能、部分代码说明、数据库设计和完整代码资源。项目涵盖了登录注册、个人信息修改、游戏商城列表查看、游戏管理、用户信息管理、数据分析等功能。代码示例包括ListView和ImageList的使用、图片上传、图表插件使用和SQL工具类封装,以及高德地图天气API的调用。
基于C#使用winform技术的游戏平台的实现【C#课程设计】
|
3月前
|
设计模式 程序员 C#
C# 使用 WinForm MDI 模式管理多个子窗体程序的详细步骤
WinForm MDI 模式就像是有超能力一般,让多个子窗体井然有序地排列在一个主窗体之下,既美观又实用。不过,也要小心管理好子窗体们的生命周期哦,否则一不小心就会出现一些意想不到的小bug
269 0
|
3月前
|
API C# Windows
【C#】在winform中如何实现嵌入第三方软件窗体
【C#】在winform中如何实现嵌入第三方软件窗体
152 0
|
3月前
|
API C#
C#实现Winform程序右下角弹窗消息提示
C#实现Winform程序右下角弹窗消息提示
163 0
|
5月前
|
关系型数据库 Java MySQL
C#winform中使用SQLite数据库
C#winform中使用SQLite数据库
223 3
C#winform中使用SQLite数据库
|
4月前
|
C# UED
C#一分钟浅谈:异步编程基础 (async/await)
在现代软件开发中,异步编程对于提升应用性能和响应性至关重要,尤其是在处理网络请求和文件读 异步编程允许程序在等待操作完成时继续执行其他任务,从而提高用户体验、高效利用资源,并增强并发性。在 C# 中,`async` 用于标记可能包含异步操作的方法,而 `await` 则用于等待异步操作完成。 示例代码展示了如何使用 `async` 和 `await` 下载文件而不阻塞调用线程。此外,本文还讨论了常见问题及解决方案,如不在 UI 线程上阻塞、避免同步上下文捕获以及正确处理异常。
58 0