09年就开了淘宝店,因为工作忙没时间打理开开停停,最近有空了,准备认真的搞一回,朋友家是卖鞋的,自己穿过他店卖的4,5双跑鞋觉得还不错,所以决定向他要货,请允许我给自己的淘宝店鞋内鞋外做个广告,有需要的朋友请多多捧场,谢谢大家~

 


在开淘宝店过程中需要批量下载图片时,利用【线程池】实现多线程【图片下载】功能,解决问题,这篇文章主要介绍此功能的实现细节。

工具主要可以细分为以下几个子部分:
1、读取excel中数据,提取数据中的图片URL
2、利用【线程池】实现多线程访问URL
3、将提交HTTP请求得到的图片保存到本地硬盘

 

1、读取excel中数据,提取数据中的图片URL

将需要进行处理的数据保存到excel文档中,作者为了图方便,就保存在第一列中,程序中访问的时候,直接读取第一列就行。

隐藏行号 复制代码 代码
  1. static DataTable ExcelToDT(string Path, string tableName) 
    
  2.        { 
    
  3.            try 
    
  4.            { 
    
  5.                string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + Path + ";" + "Extended Properties='Excel 8.0;'"; 
    
  6.                OleDbConnection conn = new OleDbConnection(strConn); 
    
  7.                conn.Open(); 
    
  8. 
    
  9.                string strExcel = ""; 
    
  10.                OleDbDataAdapter myCommand = null; 
    
  11.                DataSet ds = null; 
    
  12.                strExcel = "select * from [" + tableName + "$]"; 
    
  13. 
    
  14.                myCommand = new OleDbDataAdapter(strExcel, strConn); 
    
  15.                ds = new DataSet(); 
    
  16.                myCommand.Fill(ds, "table1"); 
    
  17.                conn.Close(); 
    
  18. 
    
  19.                return ds.Tables["table1"]; 
    
  20.            } 
    
  21.            catch 
    
  22.            { 
    
  23.                return null; 
    
  24.            } 
    
  25.        }
    
  26. 
    

2、利用【线程池】实现多线程访问URL

为了实现多线程进行HTTP请求,将所有URL装在不同的List<string>对象中,而List<string>对象装在Dictionary<int, List<string>>中,每个线程实现对一组List<string>的访问,编程过程中可以定义每组List<string>的数目,间接就定义了Dictionary有多少个键值对,有多少个线程并行提交HTTP请求。

隐藏行号 复制代码 代码
  1. static void SavePictureFromUrl() 
    
  2.      { 
    
  3.          List<string> PathList = new List<string>(); 
    
  4.          List<string> tempPathList = new List<string>(); 
    
  5.          DataTable dt = ExcelToDT("C:/b.xls", "Sheet1"); 
    
  6. 
    
  7.          int pathTempNum = 0; 
    
  8.          int DicKey = 0; 
    
  9. 
    
  10.          foreach (DataRow row in dt.Rows) 
    
  11.          { 
    
  12.              if (pathTempNum == 0) 
    
  13.              { 
    
  14.                  tempPathList = new List<string>(); 
    
  15.              } 
    
  16.              string[] a = row[0].ToString().Split(new string[] { "src", "background" }, StringSplitOptions.None); 
    
  17.              foreach (string str in a) 
    
  18.              { 
    
  19.                  if (str.Contains("jpg") || a.Contains("gif")) 
    
  20.                  { 
    
  21.                      string path = string.Empty; 
    
  22.                      path = str.Substring(str.IndexOf("http"), str.IndexOf("jpg") + str.IndexOf("gif") + 4 - str.IndexOf("http")); 
    
  23.                      if (PathList.IndexOf(path) < 0) 
    
  24.                      { 
    
  25.                          PathList.Add(path); 
    
  26.                          tempPathList.Add(path); 
    
  27.                          pathTempNum++; 
    
  28.                      } 
    
  29.                  } 
    
  30.              } 
    
  31. 
    
  32.              if (pathTempNum > 100) 
    
  33.              { 
    
  34.                  PathDic.Add(DicKey, tempPathList); 
    
  35.                  DicKey++; 
    
  36.                  pathTempNum = 0; 
    
  37.              } 
    
  38.          } 
    
  39.          ThreadPool.SetMaxThreads(100, 100); 
    
  40.          foreach (int key in PathDic.Keys) 
    
  41.          { 
    
  42.              ThreadPool.QueueUserWorkItem(new WaitCallback(SavePicFromDic), key); 
    
  43.          } 
    
  44.  }
    
  45. 
    

3、将提交HTTP请求得到的图片保存到本地硬盘

这里将图片名称保存为整个URL连接,以避免不同图片重名的可能,而文件名不可包含“/”这个符号,用“@”替代(搜了一下,文件所有链接中没有用到这个符号的)。

隐藏行号 复制代码 代码
  1. static void SavePicFromDic(object DicKey) 
    
  2.       { 
    
  3.           foreach(string path in PathDic[Convert.ToInt32( DicKey)]) 
    
  4.           { 
    
  5.               Console.WriteLine(DicKey.ToString()+path); 
    
  6.               SavePictureFromHTTP(path,@"G:\淘宝相关\图片\图片备份\" + path.Substring(7).Replace('/', '@')); 
    
  7.           }        
    
  8.       }
    
  9. 
    
  10.  
    
  11. 
    
  12.      static void SavePictureFromHTTP(string url, string path) 
    
  13.        { 
    
  14.            try 
    
  15.            { 
    
  16.                long fileLength = 0; 
    
  17. 
    
  18.                WebRequest webReq = WebRequest.Create(url); 
    
  19.                WebResponse webRes = webReq.GetResponse(); 
    
  20.                fileLength = webRes.ContentLength; 
    
  21. 
    
  22.                Stream srm = webRes.GetResponseStream(); 
    
  23.                StreamReader srmReader = new StreamReader(srm); 
    
  24.                byte[] bufferbyte = new byte[fileLength]; 
    
  25.                int allByte = (int)bufferbyte.Length; 
    
  26.                int startByte = 0; 
    
  27.                while (fileLength > 0) 
    
  28.                { 
    
  29. 
    
  30.                    int downByte = srm.Read(bufferbyte, startByte, allByte); 
    
  31.                    if (downByte == 0) { break; }; 
    
  32.                    startByte += downByte; 
    
  33.                    allByte -= downByte; 
    
  34.                } 
    
  35.                if (File.Exists(path)) 
    
  36.                { 
    
  37.                    path = path.Insert(path.LastIndexOf('.'), Guid.NewGuid().ToString()); 
    
  38.                } 
    
  39.                string tempPath = path; 
    
  40.                FileStream fs = new FileStream(tempPath, FileMode.OpenOrCreate, FileAccess.Write); 
    
  41.                fs.Write(bufferbyte, 0, bufferbyte.Length); 
    
  42.                srm.Close(); 
    
  43.                srmReader.Close(); 
    
  44.                fs.Close(); 
    
  45.            } 
    
  46.            catch (WebException ex) 
    
  47.            { 
    
  48. 
    
  49.            } 
    
  50.        }
    
  51. 
    

总结:刚开始的时候没考虑使用多线程进行保存的,而是为求简单,直接一个进程下载,用时20多分钟,后因为图片数量不断增多才考虑使用线程池进行下载,使用后效果很明显,虽然没有具体统计,但是快了好几倍是肯定的。

另外介绍下线程池与不用线程池之间的差别,个人认为,相比之下,线程池的效率效率是比较高的,因为减少了线程切换引起的上下文切换造成的资源消耗。所以这里选择了线程池,而不是简单的多线程搞定。

 

版权声明:本文原创发表于 博客园,作者为 跳跃灵魂  作者淘宝店请点击:鞋内鞋外  
本文版权归作者所有,欢迎转载,但未经作者同意须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。


作者: stubman 发表于 2011-03-16 09:47 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架