foohsinglong 发表于 2013-2-7 11:02:08

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

这张图片在winxp下缩略图显示与打开后的内容不一样,让几个同学看了一下,他们都说看过了。就是前段时间网上流传的‘一张令所有人吃惊的图片’,是一张椅子的图片,但是,如果你的系统是XP,把它下载后保存到任意一个文件夹中,打开文件夹,用缩略图的方式查看,会看到图片的缩略图是一个机器女人坐在地上。

http://dl.iteye.com/upload/attachment/300571/5fabde83-ea18-399e-b216-4a92a2f0e7ca.jpg

http://dl.iteye.com/upload/attachment/300576/fd30870a-0362-36ce-910a-e04419959779.jpg

很惊奇,但直觉的反映就是这张图片可能被存储了别的信息或修改了头文件信息。

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

public static byte[] getThumbnailImage(InputStream ip,int widthRate,int heightRate) throws IOException      //根据文件名字符串,按长宽比例放缩抽取该文件的ThumbnailImage,此方法调用到getThumbnailImage(),返回byte数组   private byte[] extractThumbnail(String fileStr,int widthRate,int heightRate)      向另一张图片写入Thumbnail的方法,用到mediautil库:   public void writeThumbnail(byte newThumbnail[],String fileStr)      主方法,thumbnailFile为要抽取缩略图信息的图片,destfile为目标图片也就是把它的缩略图用thumbnailFile信息替换掉   public void test(String thumbnailFile,String destfile){          BufferedImage buf=null;          int wRate=0;          int hRate=0;          try{            //获取destfile的长和宽,供放缩thumbnailFile使用            buf=readImage(destfile);            wRate=buf.getWidth();            hRate=buf.getHeight();          }catch(Exception e){e.printStackTrace();}          finally{            byte[] bt=extractThumbnail(thumbnailFile,wRate,hRate);//抽取thumbnailFile数据,存入bt中            writeThumbnail(bt,destfile);//向文件名为destfile的图片中写入bt中的thumbnail数据          }   ———————————————下面是全部源代码(小实验,代码写得很乱)———————————————   import java.io.*;   import java.util.Date;   import java.awt.*;   import java.awt.image.*;   import java.util.*;   import javax.imageio.*;   import javax.imageio.stream.ImageInputStream;   import mediautil.gen.directio.*;   import mediautil.gen.Log;   import java.awt.geom.AffineTransform;   import mediautil.image.ImageResources;   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;   public class TestExif {   /**         * Utility Method to get a Thumbnail Image in a byte array from an         * InputStream to a full size image. The full size image is read and scaled         * to a Thumbnail size using Java API.         */         private static byte[] getThumbnailImage(InputStream ip) throws IOException{             return getThumbnailImage(ip,0,0);         }          public static byte[] getThumbnailImage(InputStream ip,int widthRate,int heightRate) throws IOException {             ImageReader reader;             ImageInputStream iis = ImageIO.createImageInputStream(ip);             reader = (ImageReader) ImageIO.getImageReaders(iis).next();             reader.setInput(iis);             BufferedImage image = reader.read(0);             iis.close();                int t, longer, shorter;             if(widthRate>0&&heightRate>0){                  longer = widthRate;                  shorter = heightRate;             }else{                  longer = image.getWidth();                  shorter = image.getHeight();             }                               //按传入参数的长宽比例放缩            double factor = 160/(double)image.getWidth();             double factor2 =(160* (double)shorter)/((double)longer*image.getHeight());             AffineTransform tx = new AffineTransform();             tx.scale(factor, factor2);             AffineTransformOp affineOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);             image = affineOp.filter(image, null);                  // Write Out the Scaled Image to a ByteArrayOutputStream and return the bytes            ByteArrayOutputStream byteStream = new ByteArrayOutputStream(2048);            String format = "JPG";            ImageIO.write(image, format, byteStream);            System.out.println(byteStream.toByteArray());            return byteStream.toByteArray();   }         public byte[] extractThumbnail(String fileStr){          return extractThumbnail(fileStr,0,0);         }          //根据文件名字符串,按长宽比例放缩抽取该文件的ThumbnailImage,返回byte数组   private byte[] extractThumbnail(String fileStr,int widthRate,int heightRate) {       byte newThumbnail[]=null;          try{         InputStream fip = new FileInputStream(fileStr); // No need to buffer         SplitInputStream sip = new SplitInputStream(fip);         //Create a substream for LLJTran to use         InputStream subIp = sip.createSubStream();         LLJTran llj = new LLJTran(subIp);         llj.initRead(LLJTran.READ_HEADER, true, true);             sip.attachSubReader(llj, subIp);         newThumbnail= getThumbnailImage(sip,widthRate,heightRate);         sip.wrapup();         fip.close();         llj.freeMemory();         String msg = llj.getErrorMsg();         if(msg != null){            System.out.println("Error in LLJTran While Loading Image: " + msg);            Exception e = llj.getException();            if(e != null){               System.out.println("Got an Exception, throwing it..");                  throw e;               }                  System.exit(1);         }          }catch(Exception e){System.out.println("extractThumbnail"+e);}         return newThumbnail;   }   //向另一张图片写入Thumbnail的方法,用到mediautil库:   public void writeThumbnail(byte newThumbnail[],String fileStr){          try{         InputStream fip = new FileInputStream(fileStr);         LLJTran llj = new LLJTran(fip);         llj.read(LLJTran.READ_ALL,true);                      AbstractImageInfo imageInfo = llj.getImageInfo();         //important!!!!If the Image does not have an Exif Header create a dummy Exif         //Header         if(!(imageInfo instanceof Exif)){                     System.out.println("Adding a Dummy Exif Header");                     llj.addAppx(LLJTran.dummyExifHeader, 0,                                 LLJTran.dummyExifHeader.length, true);         }            //Set the new Thumbnail         if(llj.setThumbnail(newThumbnail, 0, newThumbnail.length,ImageResources.EXT_JPG))            System.out.println("Successfully Set New Thumbnail");         fip = new BufferedInputStream(new FileInputStream(fileStr));         OutputStream out = new BufferedOutputStream(new FileOutputStream("3.jpg"));         //          llj.xferInfo(fip, out, LLJTran.REPLACE, LLJTran.REPLACE);         fip.close();         out.close();         // Cleanup         llj.freeMemory();          }catch(Exception e){System.out.println("writeThumbnail"+e);}         }                public BufferedImage readImage(InputStream in,String type)throws IOException{            Iterator readers = ImageIO.getImageReadersByFormatName(type);            ImageReader reader = (ImageReader)readers.next();            ImageInputStream iis = ImageIO.createImageInputStream(in);            reader.setInput(iis,true);            BufferedImage img = reader.read(0);            return img;       }       public BufferedImage readImage(String fileName) throws IOException{            String type = fileName.substring(fileName.lastIndexOf(".")+1);            return readImage(new FileInputStream(fileName), type);       }               public void test(String thumbnailFile,String destfile){         BufferedImage buf=null;             int wRate=0;             int hRate=0;             try{            buf=readImage(destfile);            wRate=buf.getWidth();            hRate=buf.getHeight();          }catch(Exception e){e.printStackTrace();}          finally{            byte[] bt=extractThumbnail(thumbnailFile,wRate,hRate);            writeThumbnail(bt,destfile);         }         }                      public static void main(String arg[]){         TestExif t= new TestExif();            t.test("11.jpg", "22.jpg"); //用11.jpg的数据替换22.jpg的缩略图      }   }
页: [1]
查看完整版本: 缩略图与原图不一致的java实现