代码改变世界

【又拍图片相册跨分页下载图片/本地图片】合并及文字水印——淘宝开店过程中的技术应用【提供源代码下载】

2011-04-11 09:27  stubman  阅读(3484)  评论(27编辑  收藏  举报

   

    淘宝编程相关文章请阅读:

淘宝开店过程中的技术应用——【线程池】实现【图片下载】 

开淘宝店的一次大批量宝贝图片替换 

 

    闲话不多说,直接进入主题,做淘宝店的过程中,最麻烦的事情,我认为是上传产品图片并且编辑到产品描述中,就拿我拿来做示例的鞋子来说吧,一款鞋有15种配色,每种配色的有大约10张实拍图,那么这意味着有150张图片要等着处理。而这只是一个产品,一个店铺最起码也得几十个产品吧,很是麻烦。

有没有比较简单的处理方法呢,我悄悄的告诉你,我已经在用了哈~现在分享给大家,也许您没有开淘宝店,或者目前没有这个需要,不过也可以看看,说不定哪天能用到呢。我的方法是,把所有的图片竖向合并到一张图片上,那样,在编辑产品描述的时候,只需要处理一道两个图片链接了:)是不是很简单,呵呵。好吧,还有很多复杂的事情呢。就鞋类来说,很多产品的图片都是厂家或者批发商上传到图片空间中供零售商参考,我们零售的只需要拿鞋子卖就行了,问题来了,如果有150张图片在一个相册里,那么需要将这150张图片分别下载下来,然后一张张PS到一起去吗。。这可是个浩大的工程哇,就是因为这个原因,我做了这篇文章要介绍的工具《图片合并工》。

image

 

 

   

    

 

 

 

 

 

 

 

 

 此工具的功能如下:

     1、  加载整个相册下的图片【只以流格式保存在内存中】

     _1、加载指定文件夹的图片【只以流格式保存在内存中】

     2、竖向合并加载的图片

     3、向加载的图片中添加指定的文字水印

     4、根据指定的图片名称,图片高度,图片路径,保存处理之后的新图片 

    因为要处理的图片在又拍图片管家中,顾此工具只支持此相册,提供程序源代码下载,如有朋友希望将此工具应用到其他相册中,请自行修改,若涉及商业用途请联系作者本人协商,否则视为侵权。
    又拍图片管家介绍:【绝对没有广告意图,本人也是客户之一而已,因为文章相关,必须在此介绍,无法规避】

image

点击“大图”后,相册按原图大小展示图片。

image

由于每页图片数量有限制,所以会进行分页显示。

image

 

    1、加载整个相册下的图片【只以流的格式保存在内存中】

完成此功能主要子步骤介绍:

  1. 根据指定相册URL,加载对应的HTML代码
  2. HTML代码进行解读分析,提取代码中分页地址的链接
  3. 加载所有分页URL的HTML代码,并且提取其中所有产品图片的URL
  4. 根据产品图片URL加载所有图片,以流格式将图片数据保存在内存中,不用读入硬盘,减少IO

上代码:

隐藏行号 复制代码 代码
  1. /// <summary>
         
  2.  /// 所有页面中(包括下一页,1,2,3页等页面)所有大图的URL -----在又拍图片管家中,大图的命名的最后有规则,为big.jpg
         
  3.  /// </summary>
         
  4.  /// <param name="url"></param>
         
  5.  /// <returns></returns>
         
  6.  public static List<string> getAllPicUrl(string url)
         
  7.  {
         
  8.  List<string> pageUrlList = new List<string>();
         
  9.  #region 所有页得链接地址集合
         
  10.  pageUrlList.Add(url);
         
  11.  string httpCode = HttpGet(url);
         
  12. 
         
  13.  if (httpCode.IndexOf("<div class=\"pages\">") > 0)
         
  14.  {
         
  15.  string temp = httpCode.Substring(httpCode.IndexOf("<div class=\"pages\">"));
         
  16.  pageUrlList.AddRange((GetHref(temp.Substring(0, temp.IndexOf("</div>")), @"(href|src)=""(?<href>\S+?)""", "http://v.yupoo.com")));
         
  17.  }
         
  18.  #endregion
         
  19.  #region 提取每个页面中的大图URL
         
  20.  List<string> picUrlList = new List<string>();
         
  21.  foreach (string pageUrl in pageUrlList)
         
  22.  {
         
  23.  //在又拍图片管家中,大图的命名的最后有规则,为big.jpg
         
  24.  picUrlList.AddRange(GetHref(HttpGet(pageUrl), @"(href|src)=""(?<href>\S+?(big.jpg))""", ""));
         
  25.  }
         
  26.  #endregion
         
  27.  return picUrlList;
         
  28.  }
         
  29.  /// <summary>
         
  30.  /// 正则表达式提取href里面的连接 提取 所有页得链接地址集合
         
  31.  /// </summary>
         
  32.  /// <param name="html"></param>
         
  33.  /// <returns></returns>
         
  34.  public static List<String> GetHref(string html, string regexStr, string appendFrontStr)
         
  35.  {
         
  36.  List<String> links = new List<String>();
         
  37.  //MatchCollection matches = Regex.Matches(html, @"(href|src)=""(?<href>\S+?(jpg|gif|exe))""", RegexOptions.IgnoreCase);//过滤出jpg|gif|exe后缀的
         
  38.  MatchCollection matches = Regex.Matches(html, regexStr, RegexOptions.IgnoreCase);
         
  39.  foreach (Match match in matches)
         
  40.  {
         
  41.  if (links.IndexOf(appendFrontStr + match.Groups["href"].Value) < 0)
         
  42.  {
         
  43.  links.Add(appendFrontStr + match.Groups["href"].Value);
         
  44.  }
         
  45.  }
         
  46.  return links;
         
  47.  }
         

 

 

隐藏行号 复制代码 代码
  1. /// <summary>
         
  2.  /// 从指定URL加载图片数据
         
  3.  /// </summary>
         
  4.  /// <param name="imageList"></param>
         
  5.  /// <param name="maxWidth"></param>
         
  6.  /// <returns></returns>
         
  7.  ArrayList LoadPicFromUrl(ArrayList imageList, ref int maxWidth)
         
  8.  {
         
  9.  ArrayList widthList = new ArrayList();//要取出宽度最大的值,大图以最大的为准
         
  10.  
         
  11.  string youPaiUrl = txtPathFrom.Text;
         
  12. 
         
  13.  lbContentShow.Text = "正在判断需要加载的图片数量,请稍侯";
         
  14.  List<string> picUrlList = PicDealHelper.getAllPicUrl(youPaiUrl);
         
  15. 
         
  16.  int picNum = 1;
         
  17.  foreach (string picUrl in picUrlList)
         
  18.  {
         
  19.  Image tempImage = Image.FromStream(PicDealHelper.getPicStreamByUrl(picUrl));
         
  20.  imageList.Add(tempImage);
         
  21.  widthList.Add(tempImage.Width);
         
  22. 
         
  23.  lbContentShow.Text = "有 " + picUrlList.Count + "张图片需要加载,请稍侯:" + (picNum++) + "/" + picUrlList.Count;
         
  24.  }
         
  25.  maxWidth = PicDealHelper.getMaxValue(widthList);
         
  26.  
  27.  return imageList;
         
  28.  }
         

 

_1、加载指定文件夹的图片【只以流格式保存在内存中】

       此功能与功能1类似,只是不从相册加载图片,而是从本地文件夹加载,其中对本地路径进行了向下递归,选中文件夹下的子文件夹中的图片也会被一并处理,完成此功能主要子步骤介绍:

    1. 根据指定文件夹路径,递归加载文件夹下所有的图片数据
    2. 以流格式将图片数据保存在内存中,不用读入硬盘,减少IO

上代码:

隐藏行号 复制代码 代码
  1. /// <summary>
         
  2.  /// 从指定本地路径加载图片数据
         
  3.  /// </summary>
         
  4.  /// <param name="imageList"></param>
         
  5.  /// <param name="maxWidth"></param>
         
  6.  /// <returns></returns>
         
  7.  ArrayList LoadPicFromLocal(ArrayList imageList, ref int maxWidth)
         
  8.  {
         
  9.  ArrayList widthList = new ArrayList();//要取出宽度最大的值,大图以最大的为准
         
  10. 
         
  11.  string path = txtPathFrom.Text;
         
  12. 
         
  13.  List<string> fileList = GetFiles(path);
         
  14.  //lbContentShow.Text = "有 " + fileList.Count + "张图片需要加载,请稍侯:";
         
  15.  int picNum = 1;
         
  16.  foreach (string file in fileList)
         
  17.  {
         
  18.  Image tempImage = Image.FromFile(file);
         
  19.  imageList.Add(tempImage);
         
  20.  widthList.Add(tempImage.Width);
         
  21. 
         
  22.  lbContentShow.Text = "有 " + fileList.Count + "张图片需要加载,请稍侯:" + (picNum++) + "/" + fileList.Count + " ";
         
  23.  }
         
  24.  maxWidth = PicDealHelper.getMaxValue(widthList);
         
  25.  
  26.  return imageList;
         
  27.  }
         
  28. 
         
  29. 
         
  30.  /// <summary>
         
  31.  /// 递归得到指定路径下所有文件的文件路径名
         
  32.  /// </summary>
         
  33.  /// <param name="path"></param>
         
  34.  /// <returns></returns>
         
  35.  List<string> GetFiles(string path)
         
  36.  {
         
  37.  List<string> dir = Directory.GetDirectories(path).ToList();
         
  38.  List<string> files = Directory.GetFiles(path).ToList();
         
  39.  List<string> fileNames = new List<string>();
         
  40.  foreach (string file in files)
         
  41.  {
         
  42.  fileNames.Add(file);
         
  43.  }
         
  44.  foreach (string item in dir)
         
  45.  {
         
  46.  List<string> subFileNames = GetFiles(item);
         
  47.  fileNames.AddRange(subFileNames);
         
  48.  }
         
  49.  return fileNames;
         
  50.  }
         

 2、竖向合并加载的图片

3、向加载的图片中添加指定的文字水印

4、根据指定的图片名称,高度,路径,保存处理之后的新图片

 

细节分析

      如果几十张图片合并为一张,有可能会造成一张图片过大,难于打开的问题,顾程序进行了处理,在操作界面中提供一个输入图片高度的文本框,供设置生成图片的大小,如果超过大小,则另起一张图片。

      最早的版本中,有两个问题:

    A,没有对每一张要生成的图片的高度进行判断,全部以指定高度+500来进行生成,这样比较粗略,有的会空出底部的一块,还需要手动进行PS处理掉;

    B,生成的图片较大,每张都有5,6M,用户体验还是不好,不适合进行展示。

      目前的版本已经处理好这两个问题:

  1. 解决方案A:在保存图片前,将图片都加载都一个Dictionary<int, ArrayList>数据结构中,int键为要生成的图片高度,ArrayList中装载待合并的图片数据,int键如果重复(生成的图片大小正好相等),加一处理。
  2. 解决方案B:原本是newImg.Save(@savePath + picName + (i++) + ".jpg");

                           改为newImg.Save(@savePath + picName + (i++) + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

       图片体积从5M缩小到500K左右,搞定。

 

具体步骤:

    1. 根据已经加载的图片,以坐标的形式标志图片需要摆放在合并图片的位置
    2. 载入指定的水印文字,以图片的形式,载入指定的位置(此程序中写为每张图片高度的2/3处)
    3. 根据指定的保存文件的路径及图片名称,保存生成的图片

上代码:

隐藏行号 复制代码 代码
  1. private void DealPic()
         
  2.  {
         
  3.  btnDoJob.Enabled = false;
         
  4.  #region 图片参数
         
  5.  int height = 0;//图片高度(此参数根据图片数目一直累加变化)
         
  6.  int maxWidth = 0;//图片宽度(取选中图片中的最大宽度)
         
  7. 
         
  8.  string picName = txtSavePicName.Text;
         
  9.  string savePath = txtPath.Text;
         
  10.  int perHeight = Convert.ToInt32(txtHeight.Text);//处理得到的图片的每张高度
         
  11.  string WaterFont = txtWaterFont.Text;//水印文本
         
  12. 
         
  13.  ArrayList imageList = new ArrayList();//装载图片
         
  14. 
         
  15.  if (radioButton.Checked)//选择图片来源:1-又拍图片管家相册;2-本地路径;
         
  16.  {
         
  17.  imageList = LoadPicFromUrl(imageList, ref maxWidth);
         
  18.  }
         
  19.  else
         
  20.  {
         
  21.  imageList = LoadPicFromLocal(imageList, ref maxWidth);
         
  22.  }
         
  23.  #endregion
         
  24. 
         
  25.  #region 将图片进行划分,一定数量为一组,并且得出每组图片的高度
         
  26.  Dictionary<int, ArrayList> imageListDic = new Dictionary<int, ArrayList>();//键为将要保存的图片高度
         
  27.  ArrayList tempImageList = new ArrayList();
         
  28.  foreach (Image imageObj in imageList)
         
  29.  {
         
  30.  tempImageList.Add(imageObj);
         
  31. 
         
  32.  height += imageObj.Height;
         
  33.  if (height > perHeight)
         
  34.  {
         
  35.  height = getUniqueKey(height, imageListDic);//获得唯一键,不然有些图片高度相等的时候会冲突
         
  36. 
         
  37.  imageListDic.Add(height, tempImageList);
         
  38. 
         
  39.  tempImageList = new ArrayList();
         
  40.  height = 0;
         
  41.  }
         
  42.  }
         
  43.  #region 有可能遗漏掉最后一张图片,在循环结束后补上判断
         
  44.  if (height > 0)
         
  45.  {
         
  46.  imageListDic.Add(height, tempImageList);
         
  47.  tempImageList = new ArrayList();
         
  48.  height = 0;
         
  49.  }
         
  50.  #endregion
         
  51.  #endregion
         
  52. 
         
  53.  int i = 1;//图片数量标志
         
  54. 
         
  55.  #region 处理,并保存图片
         
  56.  foreach (KeyValuePair<int, ArrayList> dicObject in imageListDic)
         
  57.  {
         
  58.  height = 0;
         
  59. 
         
  60.  Bitmap newImg = new Bitmap(maxWidth, dicObject.Key);
         
  61.  Graphics g = Graphics.FromImage(newImg);
         
  62. 
         
  63.  g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
         
  64.  g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
         
  65. 
         
  66.  g.Clear(Color.White);//清空图片,为下一次载入做准备
         
  67. 
         
  68.  foreach (Image imageObj in dicObject.Value)
         
  69.  {
         
  70.  g.DrawImage(imageObj, new Rectangle((maxWidth - imageObj.Width) / 2, height, imageObj.Width, imageObj.Height));
         
  71. 
         
  72.  #region 上水印
         
  73.  Font fontObj = new Font("Verdana", 20);//设置字体与字号,按具体需要来设,这里只是个例子
         
  74.  Brush brushColor = new SolidBrush(Color.Black);//水印文字的颜色
         
  75.  g.DrawString(WaterFont, fontObj, brushColor, 30, height + imageObj.Height * 2 / 3);//后面两个10,表示的是文字的起始坐标
         
  76.  #endregion
         
  77.  height += imageObj.Height;
         
  78. 
         
  79.  }
         
  80. 
         
  81.  newImg.Save(@savePath + picName + (i++) + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
         
  82.  g.Dispose();
         
  83. 
         
  84.  lbContentShow.Text = "保存" + i + "张图片。";
         
  85.  }
         
  86. 
         
  87.  lbContentShow.Text = "处理结束。";
         
  88. 
         
  89.  btnDoJob.Enabled = true;
         
  90.  #endregion
         
  91.  }
         

 

工具使用,生成图片效果截图 :

image

图片处理中。。。

image

生成图片列表截图:

image

整张图片及局部截图:

image                                          image

 

这样,水印就打上了,图片也整理成一大张的了,600K左右一张图片展示在产品描述中也快多了。

这个工具为我省了不少事情,我也推荐给好几个开淘宝店认识的朋友,大家都很用的上,希望能给有这方面需要的朋友一些帮助~~ 

  

源代码下载:图片合并工 

因为比较忙,开发这个小工具总共也就抽了半天多的时间,由于想发给大家一起用用,还专门花了个把小时把项目由控制台格式的调整为Winform的,请高手们不要过于强调代码质量,有板砖请轻拍。 

也希望下载使用并且修改了本工具的程序员朋友能够将自己修改的版本发上来,有什么好的功能,大家一起使用哈~ 

 

觉得看了文章有所收获的朋友也请动动您的食指,帮忙点下推荐哈,谢谢大家!