论坛首页 Java版

缩略图与原图不一致的java实现

浏览 7339 次
该帖已经被评为良好帖
作者 正文
最后更新时间:2008-03-07 关键字: java, thumbnail, 缩略图, 图片, mediautil

      昨天早上朋友传给我一张图片,曾被此君的一些恶作剧图片吓倒过,这次刚开始也不敢打开。这张图片在winxp下缩略图显示与打开后的内容不一样,让几个同学看了一下,他们都说看过了。就是前段时间网上流传的‘一张令所有人吃惊的图片’,是一张椅子的图片,但是,如果你的系统是XP,把它下载后保存到任意一个文件夹中,打开文件夹,用缩略图的方式查看,会看到图片的缩略图是一个机器女人坐在地上。
原图:缩略图:

很惊奇,但直觉的反映就是这张图片可能被存储了别的信息或修改了头文件信息。
        经过一番研究之后,知道了原理:JPEG标准在文件中记录了一些EXIF信息,缩略图是一幅较小的JPEG图片,存储在EXIF信息段。而Windows在第一次显示缩略图时先读当前目录中的"Thumbs.db"这个文件,其实这是一个缩略图数据库,从而来判断是否有该图片的缩略图。如果不存在"Thumbs.db"文件或者该库中不存在该图片的缩略图,那么Windows会尝试取图片中的EXIF信息,判断是否存在缩略图数据。如果图片中EXIF信息中不存在缩略图信息或信息错误,那么Windows就会用插值的方法重新生成缩略图(如果可能则保存到当前目录中的"Thumbs.db"缩略图数据库中)。 对于修改缩略图方法有用ultraEdit直接编辑文件替换EXIF信息或用exifer这样的工具,这不是本文关心的内容,本文介绍用java实现的方法。
        程序把一张jpg格式图片的缩略图用另一张图的内容替换,实现的效果跟上面一样
原图:缩略图:

        实现原理就是抽取一张图片的信息,然后替换掉另一张图片EXIF的Thumbnail。注意抽取的图片长宽比例要与原图一致,否则没有效果,在程序中我已经自动判断并调整了。下面是抽取信息的一个主要方法:

java 代码
  1. public static byte[] getThumbnailImage(InputStream ip,int widthRate,int heightRate) throws IOException    
  2.   
  3. //根据文件名字符串,按长宽比例放缩抽取该文件的ThumbnailImage,此方法调用到getThumbnailImage(),返回byte数组    
  4. private byte[] extractThumbnail(String fileStr,int widthRate,int heightRate)    
  5.   
  6. 向另一张图片写入Thumbnail的方法,用到mediautil库:    
  7. public void writeThumbnail(byte newThumbnail[],String fileStr)    
  8.   
  9. 主方法,thumbnailFile为要抽取缩略图信息的图片,destfile为目标图片也就是把它的缩略图用thumbnailFile信息替换掉    
  10. public void test(String thumbnailFile,String destfile){    
  11.      BufferedImage buf=null;    
  12.      int wRate=0;    
  13.      int hRate=0;    
  14.      try{    
  15.          //获取destfile的长和宽,供放缩thumbnailFile使用    
  16.          buf=readImage(destfile);    
  17.          wRate=buf.getWidth();    
  18.          hRate=buf.getHeight();    
  19.      }catch(Exception e){e.printStackTrace();}    
  20.      finally{    
  21.          byte[] bt=extractThumbnail(thumbnailFile,wRate,hRate);//抽取thumbnailFile数据,存入bt中    
  22.          writeThumbnail(bt,destfile);//向文件名为destfile的图片中写入bt中的thumbnail数据    
  23.      }    
  24. ———————————————下面是全部源代码(小实验,代码写得很乱)———————————————    
  25. import java.io.*;    
  26. import java.util.Date;    
  27. import java.awt.*;    
  28. import java.awt.image.*;    
  29. import java.util.*;    
  30. import javax.imageio.*;    
  31. import javax.imageio.stream.ImageInputStream;    
  32. import mediautil.gen.directio.*;    
  33. import mediautil.gen.Log;    
  34. import java.awt.geom.AffineTransform;    
  35. import mediautil.image.ImageResources;    
  36. import mediautil.image.jpeg.LLJTran;    
  37. import mediautil.image.jpeg.AbstractImageInfo;    
  38. import mediautil.image.jpeg.Exif;    
  39. import mediautil.image.jpeg.Entry;    
  40. import mediautil.image.jpeg.LLJTranException;    
  41. public class TestExif {    
  42. /**   
  43.      * Utility Method to get a Thumbnail Image in a byte array from an   
  44.      * InputStream to a full size image. The full size image is read and scaled   
  45.      * to a Thumbnail size using Java API.   
  46.      */    
  47.     private static byte[] getThumbnailImage(InputStream ip) throws IOException{    
  48.         return getThumbnailImage(ip,0,0);    
  49.     }    
  50.     
  51. public static byte[] getThumbnailImage(InputStream ip,int widthRate,int heightRate) throws IOException {    
  52.         ImageReader reader;    
  53.         ImageInputStream iis = ImageIO.createImageInputStream(ip);    
  54.         reader = (ImageReader) ImageIO.getImageReaders(iis).next();    
  55.         reader.setInput(iis);    
  56.         BufferedImage image = reader.read(0);    
  57.         iis.close();    
  58.   
  59.         int t, longer, shorter;    
  60.         if(widthRate>0&&heightRate>0){    
  61.              longer = widthRate;    
  62.              shorter = heightRate;    
  63.         }else{    
  64.              longer = image.getWidth();    
  65.              shorter = image.getHeight();    
  66.         }    
  67.                   
  68.        //按传入参数的长宽比例放缩    
  69.        double factor = 160/(double)image.getWidth();    
  70.         double factor2 =(160* (double)shorter)/((double)longer*image.getHeight());    
  71.         AffineTransform tx = new AffineTransform();    
  72.         tx.scale(factor, factor2);    
  73.         AffineTransformOp affineOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);    
  74.         image = affineOp.filter(image, null);   
  75.     
  76.          // Write Out the Scaled Image to a ByteArrayOutputStream and return the bytes    
  77.          ByteArrayOutputStream byteStream = new ByteArrayOutputStream(2048);    
  78.          String format = "JPG";    
  79.          ImageIO.write(image, format, byteStream);    
  80.          System.out.println(byteStream.toByteArray());    
  81.          return byteStream.toByteArray();    
  82. }    
  83.     public byte[] extractThumbnail(String fileStr){    
  84.      return extractThumbnail(fileStr,0,0);    
  85.     }    
  86.     
  87. //根据文件名字符串,按长宽比例放缩抽取该文件的ThumbnailImage,返回byte数组    
  88. private byte[] extractThumbnail(String fileStr,int widthRate,int heightRate) {    
  89.   byte newThumbnail[]=null;    
  90.      try{    
  91.       InputStream fip = new FileInputStream(fileStr); // No need to buffer    
  92.       SplitInputStream sip = new SplitInputStream(fip);    
  93.       //Create a substream for LLJTran to use    
  94.       InputStream subIp = sip.createSubStream();    
  95.       LLJTran llj = new LLJTran(subIp);    
  96.       llj.initRead(LLJTran.READ_HEADER, truetrue);      
  97.       sip.attachSubReader(llj, subIp);    
  98.       newThumbnail= getThumbnailImage(sip,widthRate,heightRate);    
  99.       sip.wrapup();    
  100.       fip.close();    
  101.       llj.freeMemory();    
  102.       String msg = llj.getErrorMsg();    
  103.       if(msg != null){    
  104.          System.out.println("Error in LLJTran While Loading Image: " + msg);    
  105.          Exception e = llj.getException();    
  106.          if(e != null){    
  107.             System.out.println("Got an Exception, throwing it..");    
  108.              throw e;    
  109.           }    
  110.              System.exit(1);    
  111.       }    
  112.      }catch(Exception e){System.out.println("extractThumbnail"+e);}    
  113.       return newThumbnail;    
  114. }    
  115. //向另一张图片写入Thumbnail的方法,用到mediautil库:    
  116. public void writeThumbnail(byte newThumbnail[],String fileStr){    
  117.      try{    
  118.       InputStream fip = new FileInputStream(fileStr);    
  119.       LLJTran llj = new LLJTran(fip);    
  120.       llj.read(LLJTran.READ_ALL,true);    
  121.           
  122.       AbstractImageInfo imageInfo = llj.getImageInfo();    
  123.       //important!!!!  If the Image does not have an Exif Header create a dummy Exif    
  124.       //Header    
  125.       if(!(imageInfo instanceof Exif)){    
  126.                 System.out.println("Adding a Dummy Exif Header");    
  127.                 llj.addAppx(LLJTran.dummyExifHeader, 0,    
  128.                             LLJTran.dummyExifHeader.length, true);    
  129.       }    
  130.   
  131.       //  Set the new Thumbnail    
  132.       if(llj.setThumbnail(newThumbnail, 0, newThumbnail.length,ImageResources.EXT_JPG))    
  133.        System.out.println("Successfully Set New Thumbnail");    
  134.       fip = new BufferedInputStream(new FileInputStream(fileStr));    
  135.       OutputStream out = new BufferedOutputStream(new FileOutputStream("3.jpg"));    
  136.       //   
  137.       llj.xferInfo(fip, out, LLJTran.REPLACE, LLJTran.REPLACE);    
  138.       fip.close();    
  139.       out.close();    
  140.       // Cleanup    
  141.       llj.freeMemory();    
  142.      }catch(Exception e){System.out.println("writeThumbnail"+e);}    
  143.     }    
  144.         
  145.   public BufferedImage readImage(InputStream in,String type)throws IOException{    
  146.        Iterator readers = ImageIO.getImageReadersByFormatName(type);    
  147.        ImageReader reader = (ImageReader)readers.next();    
  148.        ImageInputStream iis = ImageIO.createImageInputStream(in);    
  149.        reader.setInput(iis,true);    
  150.        BufferedImage img = reader.read(0);      
  151.        return img;    
  152.   }    
  153.   public BufferedImage readImage(String fileName) throws IOException{    
  154.        String type = fileName.substring(fileName.lastIndexOf(".")+1);    
  155.        return readImage(new FileInputStream(fileName), type);    
  156.   }    
  157.         
  158.    public void test(String thumbnailFile,String destfile){         BufferedImage buf=null;    
  159.         int wRate=0;    
  160.         int hRate=0;    
  161.         try{    
  162.          buf=readImage(destfile);    
  163.          wRate=buf.getWidth();    
  164.          hRate=buf.getHeight();    
  165.      }catch(Exception e){e.printStackTrace();}    
  166.      finally{    
  167.          byte[] bt=extractThumbnail(thumbnailFile,wRate,hRate);    
  168.          writeThumbnail(bt,destfile);    
  169.       }      
  170.     }    
  171.             
  172.     public static void main(String arg[]){    
  173.       TestExif t= new TestExif();    
  174.        t.test("11.jpg""22.jpg"); //用11.jpg的数据替换22.jpg的缩略图   
  175.     }    
  176. }    

 

 

 

Java架构俱乐部,QQ群2826942 ,交流Java架构,领域模型,设计模式,AOP,IOC,轻量级,DDD,项目管理,设计思想等问题,聚集经验丰富的高级程序员,架构师,项目经理等交流以上层面较高的问题。欢迎各位有识之士加入,共同探讨、发表高见、广结精英。为了使各位成员免受无聊信息和低端问题的打扰,请尽量避免讨论代码实现等入门级问题,维护本群的氛围和讨论质量。

  • F8574a14-fd07-3023-bf29-667856d3982e-thumb
  • 描述:
  • 大小: 3.8 KB
  • 856bfdc1-4c49-37bc-83c2-b4c9532c34de-thumb
  • 描述:
  • 大小: 43.4 KB
  • 5aaa99cf-69c0-3982-90a0-296bc0e0a42f-thumb
  • 描述:
  • 大小: 15.2 KB
   
最后更新时间:2007-07-30
第一次发现这样的情况,见识了!
   
0 请登录后投票
最后更新时间:2007-07-30
用imageMagick随便搞
   
0 请登录后投票
最后更新时间:2007-07-30
这个挺好玩的,以前只是知道原理
   
0 请登录后投票
最后更新时间:2007-07-30
很好的文章,原理与代码结合
   
0 请登录后投票
最后更新时间:2007-07-31
hama 写道
用imageMagick随便搞

我也知道用图像编辑工具可以搞,本文主要说的是java的编程实现
   
0 请登录后投票
最后更新时间:2007-07-31
用到mediautil库哪个库?好象metadata-extractor-2.2.1.jar等版本都没有该包.
import mediautil.image.jpeg.LLJTran;
import mediautil.image.jpeg.AbstractImageInfo;
import mediautil.image.jpeg.Exif;
import mediautil.image.jpeg.Entry;
import mediautil.image.jpeg.LLJTranException;
   
0 请登录后投票
最后更新时间:2007-07-31
xuelange 写道
用到mediautil库哪个库?好象metadata-extractor-2.2.1.jar等版本都没有该包.
import mediautil.image.jpeg.LLJTran;
import mediautil.image.jpeg.AbstractImageInfo;
import mediautil.image.jpeg.Exif;
import mediautil.image.jpeg.Entry;
import mediautil.image.jpeg.LLJTranException;


这个库,metadata-extractor听说是只能读不能写的,我没试过
   
0 请登录后投票
最后更新时间:2007-08-01
JPEG的标准里面说得很清楚的.
   
0 请登录后投票
最后更新时间:2007-08-02

我用了两张.jpg图片做实验

也用了你文中这两张图(载下来,也是.jpg)做实验,程序也跑通了,不知道为何没有看到效果(即图片缩略图没改变)?

System.out.println
  1. Frame, precision 8  
  2. X= 800, Y= 600  
  3. Components 3 (null)   
  4. Size in MCU 50x38   
  5. [B@1827284  
  6. Frame, precision 8  
  7. X= 600, Y= 450  
  8. Components 3 (null)   
  9. Size in MCU 38x29   
  10. 0xad94(44436byte(s) read in Unknown/Stream   
  11. Successfully Set New Thumbnail   






   
0 请登录后投票
论坛首页 Java版

跳转论坛:
JavaEye推荐