zoukankan      html  css  js  c++  java
  • 淘宝开店过程中的技术应用——【线程池】实现【图片下载】



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

    工具主要可以细分为以下几个子部分:
    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. 
           
    27. 
           

    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. 
           
    46. 
           

    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. 
           
    52. 
           

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

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

  • 相关阅读:
    React-Native 基本环境的搭建
    initWithFrame 与 initWithCoder 、awakeFromNib 的方法理解笔记
    关于 jwTextFiled 的使用说明
    使用 SourceTree 遇到冲突的解决方法
    如何使用最简单的方法将一个已经存在的工程中使用 cocaPodfile
    使用 NSData 分类实现,对 NSData 数据类型进行 AES 加密
    相机检测
    纯代码适配优化方案之一(内联函数的使用)
    页面跳转问题,多次 push 到新的页面的问题的解决方法
    判断银行卡卡号输入的合法性接口
  • 原文地址:https://www.cnblogs.com/stubman/p/1979475.html
Copyright © 2011-2022 走看看