JAI 多图片合成TIF格式
因为项目需要,所以要做这么个工具类,发现了一些问题,接下来一一说明。
需要引入jai-codec-1.1.3.jar跟jai_code-1.1.3.jar。
1.判断图片格式:
JPEG (jpg),文件头:FFD8FF ,结尾:FFD9
PNG (png),文件头:89504E47
GIF (gif),文件头:47494638
TIFF (tif),文件头:49492A00
Windows Bitmap (bmp),文件头:424D
-- 可以通过UltraEdit进行查看图片的十六进制内容
/** * 判断图片格式 * @param fis * @return */ private static String getPicType(FileInputStream fis) { //读取文件的前几个字节来判断图片格式 byte[] b = new byte[4]; try { fis.read(b, 0, b.length); String type = bytesToHexString(b).toUpperCase(); if (type.contains("FFD8FF")) { return "jpg"; } else if (type.contains("89504E47")) { return "png"; } else if (type.contains("47494638")) { return "gif"; } else if (type.contains("424D")) { return "bmp"; }else{ return "unkown"; } } catch (IOException e) { e.printStackTrace(); } finally { if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } private static String bytesToHexString(byte[] src){ StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); }
2.多张图片合成TIF,因为合成之后可能文件会很大,所以采用了压缩模式(TIFFEncodeParam.COMPRESSION_JPEG_TTN2),但是压缩模式需要将图片都转为JPG格式
/** * * @param imageFileList 图片路径列表 (图片为E:/xx/xx.jpg格式) * @param toPath tif文件所放路径 * @param distFileName tif文件名 * @param convertPath 转换图片存放路径 * @param isCompress 是否压缩(压缩的时候是先将不是jpg的格式转为jpg,因为JAI可以设置JPEG的压缩模式。 * 但是png转jpg会有点失真,所以建议用非压缩的方式!!!) */ public static String manyImgToTif(List<String> imageFileList,String toPath, String distFileName, String convertPath, boolean isCompress) { String tifFile = null; if(imageFileList != null && imageFileList.size()>0){ List<File> fileArr = new ArrayList<File>(); String fileName; String imageFile; String fileType = ""; File tmpFile ; FileInputStream tmpIns = null; boolean isNeedTransfer = false; for (int i = 0; i < imageFileList.size(); i++){ imageFile = imageFileList.get(i); String[] tempFile = imageFile.split("/"); fileName = tempFile[tempFile.length-1]; tmpFile = new File(imageFile); // 处理文件 try { // 以防是png格式的图片直接改后缀的情况 tmpIns = new FileInputStream(tmpFile); fileType = getPicType(tmpIns); if(isCompress){ File convertFile = new File(convertPath); if (!convertFile.exists()) { convertFile.mkdirs(); } if ("bmp".equals(fileType) || "jpg".equals(fileType)){ // 有时图片有损坏,但是能展示,就是用这个架包的时候会报错, // 比较严格,所以需要捕获异常做一下转换 isNeedTransfer = false; try{ // 需要重新获取流,因为上面判断格式已经读过流了. tmpIns = new FileInputStream(tmpFile); JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(tmpIns); decoder.decodeAsBufferedImage(); } catch (TruncatedFileException e){ e.printStackTrace(); logger.info("图片有损坏,需要做转换,image:"+imageFile); isNeedTransfer = true; } if(isNeedTransfer){ fileArr.add(pngtoJpg(imageFile,convertPath,fileName)); } else { fileArr.add(new File(imageFile)); } } else if("gif".equals(fileType)){ fileArr.add(giftoJpg(imageFile,convertPath,fileName)); }else if("png".equals(fileType)){ fileArr.add(pngtoJpg(imageFile,convertPath,fileName)); } } else { if ("bmp".equals(fileType) || "jpg".equals(fileType) || "gif".equals(fileType) || "png".equals(fileType)){ fileArr.add(new File(imageFile)); } } } catch (Exception e) { e.printStackTrace(); } finally { if(tmpIns != null){ try { tmpIns.close(); } catch (IOException e) { e.printStackTrace(); } } } } if (fileArr.size() > 0) { try { ArrayList pages = new ArrayList(fileArr.size() - 1); FileSeekableStream[] stream = new FileSeekableStream[fileArr.size()]; for (int i = 0; i < fileArr.size(); i++) { stream[i] = new FileSeekableStream(fileArr.get(i).getCanonicalPath()); } ParameterBlock pb = (new ParameterBlock()); PlanarImage firstPage = JAI.create("stream", stream[0]); for (int i = 1; i < fileArr.size(); i++) { PlanarImage page = JAI.create("stream", stream[i]); pages.add(page); } TIFFEncodeParam param = new TIFFEncodeParam(); if(isCompress){ param.setCompression(TIFFEncodeParam.COMPRESSION_JPEG_TTN2); } TIFFField[] extras = new TIFFField[4]; extras[0] = new TIFFField(262, TIFFField.TIFF_SHORT, 1, (Object) new short[] { 6 }); extras[1] = new TIFFField(282, 5, 1, (Object) new long[][]{{(long) 200, 1}, {0, 0}}); extras[2] = new TIFFField(283, 5, 1, (Object) new long[][]{{(long) 200, 1}, {0, 0}}); extras[3] = new TIFFField(258, TIFFField.TIFF_SHORT, 1, (Object) new char[] { 8 }); param.setExtraFields(extras); param.setExtraImages(pages.iterator()); File f = new File(toPath); if (!f.exists()) { f.mkdirs(); } tifFile = toPath + "\\"+ distFileName+".tif"; OutputStream os = new FileOutputStream(tifFile); ImageEncoder enc = ImageCodec.createImageEncoder("tiff", os, param); enc.encode(firstPage); //关掉流 os.flush(); os.close(); for (int i = 0; i < fileArr.size(); i++) { stream[i].close(); } logger.info("====manyImgToTif done===="); } catch (IOException e) { e.printStackTrace(); } } } return tifFile; }
3.遇到的问题:
java.lang.ArrayIndexOutOfBoundsException: 3
at com.sun.media.jai.codec.JPEGEncodeParam.getHorizontalSubsampling(JPEGEncodeParam.java:104)
这个是因为图片不都是JPG格式,但却采用了TIFFEncodeParam.COMPRESSION_JPEG_TTN2压缩模式导致的出错,转为JPG格式就好了。
java.lang.RuntimeException: - Unable to render RenderedOp for this operation. at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:827) Caused by: com.sun.image.codec.jpeg.ImageFormatException: Not a JPEG file: starts with 0xff 0xd9
这个是因为图片有损坏,例如JPG格式,文件头是FFD8FF ,但可能结尾不是FFD9,所以导致处理的时候会有问题,所以需要重读取流再转换一下就正常了。
附录-两个转换方法(png->jpg,bmp->jpg)
public static File pngtoJpg(String fromImg, String path , String fileName) { File outFile = new File(path+ File.separator+fileName+"_convert.jpg"); try { FileOutputStream out = new FileOutputStream(outFile); File img = new File(fromImg); BufferedImage image = ImageIO.read(img); BufferedImage newBufferedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); //TYPE_INT_RGB:创建一个RBG图像,24位深度,成功将32位图转化成24位 newBufferedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null); ImageIO.write(newBufferedImage,"jpg",outFile); out.flush(); out.close(); }catch (IOException e){ e.printStackTrace(); } return outFile; } public static File giftoJpg(String fromImg, String path, String fileName) { File outFile = new File(path+ File.separator+fileName+"_convert.jpg"); try { File infile = new File(fromImg); BufferedImage src = null; src = ImageIO.read(infile); int wideth = src.getWidth(null); int height = src.getHeight(null); BufferedImage tag = new BufferedImage(wideth , height , BufferedImage.TYPE_INT_RGB); tag.getGraphics().drawImage(src, 0, 0, wideth , height , null); FileOutputStream out = new FileOutputStream(outFile); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); encoder.encode(tag); out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } return outFile; }