java圖片動態添加水印(利用Graphics2D)

大家可能都對水印有了解,但是一般的水印都是事先生成的圖片,比如

右下角會有一個水印logo,用來標明圖片的出處。

但是如果有一個需求是,需要對某個網站的信息進行加密,對於每個信息都需要針對瀏覽者進行動態水印的添加,也就是瀏覽的時候才生成,如下圖:




這時,我們就需要借用java中的2D畫筆,來進行這個過程,代碼如下:

public class WordIcon {

	private static String CSSCOLOR = "#bf8f79";
	private static float ALPHA = 0.4f; 
	
    public static ImgStreamBO createMark(WaterMarkBO waterMarkBO) {
    	BufferedImage bimage = buildGraph2d(waterMarkBO);
    	ImgStreamBO imgStreamBO = outputPic(bimage);
    	String imgName = waterMarkBO.getFileUrl().substring(waterMarkBO.getFileUrl().lastIndexOf("/")+1, waterMarkBO.getFileUrl().length());
    	imgStreamBO.setFileName(imgName+waterMarkBO.getUserId());
    	return imgStreamBO;
    }

    //構造2d畫筆
	private static BufferedImage buildGraph2d(WaterMarkBO waterMarkBO){
		Color markContentColor = Color.gray;
        URL url;
		try {
			url = new URL(waterMarkBO.getFileUrl());
		} catch (MalformedURLException e) {
			LoggerHelper.err(WordIcon.class, "pic url error");
			return null;
		}
        ImageIcon imgIcon = new ImageIcon(url);
        Image theImg = imgIcon.getImage();
        //Image可以獲得 輸入圖片的信息
        int width = theImg.getWidth(null);
        int height = theImg.getHeight(null);
        //爲畫出圖片的大小
        BufferedImage bimage = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
        //2d 畫筆
        Graphics2D g = bimage.createGraphics();
        g.setColor(markContentColor);
        g.setBackground(Color.white);

        //畫出圖片-----------------------------------
        g.drawImage(theImg, 0, 0, null);

        //--------對要顯示的文字進行處理--------------
        AttributedCharacterIterator iter = buildFont(waterMarkBO.getMarkContent(), waterMarkBO.getFontType(),
        		waterMarkBO.getFontSize());
        Color color = Color.decode(CSSCOLOR);
        g.setColor(color);
        if (null != waterMarkBO.getDegree()) {
            // 設置水印旋轉
            g.rotate(Math.toRadians(waterMarkBO.getDegree()),
                    (double) bimage.getWidth() / 2, (double) bimage
                    .getHeight() / 2);
        }
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
        		ALPHA));
        for(int i=0;i<9;i++){
           for(int j = 0;j<6;j++){
               g.drawString(iter, (int) (width - (width*j/4)), (int) (height - (height*i/8)));
           }
        }
        //添加水印的文字和設置水印文字出現的內容 ----位置
        g.dispose();//畫筆結束
		return bimage;
	}
	
	 //輸出文件
		private static ImgStreamBO outputPic(BufferedImage bimage) {
			ByteArrayOutputStream out = null;
			ImgStreamBO imgStreamBO = new ImgStreamBO();
			try {
	            //輸出 文件 到指定的路徑
	            out = new ByteArrayOutputStream();
	            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
	            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bimage);
	            param.setQuality(1, true);
	            encoder.encode(bimage, param);
	            byte[] buff = out.toByteArray();
	            imgStreamBO.setInput(new ByteArrayInputStream(buff));
	            imgStreamBO.setLength((long)buff.length);
	            return imgStreamBO;
	        } catch (Exception e) {
	        	LoggerHelper.err(WordIcon.class, "get inputStream error");
	            return null;
	        }finally{
	        	if(out!=null){
	        		try {
						out.close();
					} catch (IOException e) {
						LoggerHelper.err(WordIcon.class, "close outputStream error");
					}
	        	}
	        }
		}

	//形成字體屬性
	private static AttributedCharacterIterator buildFont(String markContent,
			String fontType, int fontSize) {
		AttributedString ats = new AttributedString(markContent);
        Font f = new Font(fontType, Font.PLAIN, fontSize);
        ats.addAttribute(TextAttribute.FONT, f, 0, markContent.length());
        AttributedCharacterIterator iter = ats.getIterator();
		return iter;
	}
}

這個類傳入的參數爲一個封裝好的BO,代碼爲:

public class WaterMarkBO {

	private String fileUrl;
	private String markContent;
	private String fontType;
	private int fontSize;
	private Integer degree;
	private Long userId;
	
	public WaterMarkBO(){
		
	}
	
	public WaterMarkBO(String fileUrl, String markContent, String fontType, int fontSize, Integer degree,Long userId){
		this.fileUrl = fileUrl;
		this.markContent = markContent;
		this.fontType = fontType;
		this.fontSize = fontSize;
		this.degree = degree;
		this.userId = userId;
	}
	
	……省略get,set方法
		
	}
這樣的話,就可以動態的給圖片添加水印。

如果是要給新聞添加水印的話,我們會選擇將文章word轉爲pdf,然後再後臺先將pdf轉爲圖片,這樣就可以保證前面的圖片來源了。

附上將pdf轉爲圖片的類:

public class PDFchangToImage {
    public static List<String> changePdfToImg(String serverPath,String filePath,long articleId) {
    	List<String> picUrl = new ArrayList<String>();
        try {
        	String path = serverPath
            + File.separatorChar;
            File file = new File(filePath);
            RandomAccessFile raf = new RandomAccessFile(file, "r");
            FileChannel channel = raf.getChannel();
            MappedByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
            PDFFile pdffile = new PDFFile(buf);
            for (int i = 1; i <= pdffile.getNumPages(); i++) {
                PDFPage page = pdffile.getPage(i);
                Rectangle rect = new Rectangle(0, 0, ((int) page.getBBox().getWidth()), ((int) page.getBBox().getHeight()));
                int n = 2;/**圖片清晰度(n>0且n<7)【pdf放大參數】*/
                Image img = page.getImage(rect.width * n, rect.height * n, rect,  /**放大pdf到n倍,創建圖片。*/null, /**null for the ImageObserver  */true, /** fill background with white  */true /** block until drawing is done  */);
                BufferedImage tag = new BufferedImage(rect.width * n, rect.height * n, BufferedImage.TYPE_INT_RGB);
                tag.getGraphics().drawImage(img, 0, 0, rect.width * n, rect.height * n, null);          /** File imgfile = new File("D:\\work\\mybook\\FilesNew\\img\\" + i + ".jpg");          if(imgfile.exists()){                if(imgfile.createNewFile())                {                    System.out.println("創建圖片:"+"D:\\work\\mybook\\FilesNew\\img\\" + i + ".jpg");                } else {                    System.out.println("創建圖片失敗!");                }            }  */
                String singleFilePath = path + articleId + i +".jpg";
                FileOutputStream out = new FileOutputStream(singleFilePath); /** 輸出到文件流*/
                picUrl.add(singleFilePath);
                JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
                JPEGEncodeParam param2 = encoder.getDefaultJPEGEncodeParam(tag);
                param2.setQuality(1f, true);/**1f~0.01f是提高生成的圖片質量  */encoder.setJPEGEncodeParam(param2);
                encoder.encode(tag);/** JPEG編碼  */out.close();
            }
            channel.close();
            raf.close();
            unmap(buf);/**如果要在轉圖片之後刪除pdf,就必須要這個關閉流和清空緩衝的方法*/
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return picUrl;
    }

    private static void unmap(final Object buffer) {
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                try {
                    Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
                    getCleanerMethod.setAccessible(true);
                    sun.misc.Cleaner cleaner = (sun.misc.Cleaner) getCleanerMethod.invoke(buffer, new Object[0]);
                    cleaner.clean();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }
}

如果覺得前面進行瀏覽時,速度跟不上,可以使用多線程和futuretask來幫你快速的完成圖片處理。我試過多線程處理響應速度一般也就是1秒多一點點,基本處於可以接受的狀態,而且如果覺得這樣還不行,你可以考慮使用緩存文件系統,將生成的圖片存儲起來,下次如果發現有就不用再生成了。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章