Bug消灭记:透明 png 图片加水印后变成黑问题

背景

本周四晚下班后,领导火急火燎地电话我,说公司某个产品的系统配置模块在配置登录 Logo 的时候,上传一个透明底色的 png 图片,结果变成了黑色背景色,让我修复一下这个 Bug 。

领导初步怀疑是后台转存图片时,png 和 jpg 图片格式不一致、存储受损导致的。周五上午复现了这个问题,最后定位到了是透明 png 图片在使用 Java 的图像处理工具时普遍存在的问题,需要额外加两行处理才能解决。

问题跟踪

系统配置的 Logo 设置的流程是这样的:

  1. 文件上传:后台校验文件格式,格式错误返回;否则,就对图片重命名,统一转存为 png 格式图片,并为图片添加水印
  2. 配置存储:存储 logo 配置文件路径信息。

调试代码,上传透明 png 图片后,中间一步转存图片的逻辑,看后台存储的图片正常,再放开添加水印的图片后,转存的图片多了黑色背景,确定问题出在加水印的代码上。

解决办法

为图片添加水印的代码是从网上搜来的:

/**
     * 对图片添加水印,以过滤恶意代码
     * @param srcImg 源图片
     * @param waterImg  水印图片
     * @param x
     * @param y
     * @param alpha  透明度,0 为添加透明水印
     * @throws IOException
     */
    public final  void addWaterMark(String srcImg, String waterImg, int x, int y, float alpha) throws IOException {  
        // 加载目标图片  
        File file = new File(srcImg);  
        String ext = srcImg.substring(srcImg.lastIndexOf(".") + 1);  
        Image image = ImageIO.read(file);  
        int width = image.getWidth(null);  
        int height = image.getHeight(null);  
   
        // 创建空白画布
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);  
        Graphics2D g = bufferedImage.createGraphics();  
        
        // 下面两句话,解决 png 透明图片加水印后变黑的问题
        bufferedImage = g.getDeviceConfiguration().createCompatibleImage(image.getWidth(null), image.getHeight(null), Transparency.TRANSLUCENT);
        g = bufferedImage.createGraphics();
        
        // 绘制源图片
        g.drawImage(image, 0, 0, width, height, null);  
   
        // 加载水印图片。  
        Image waterImage = ImageIO.read(new File(waterImg));  
        int width_1 = waterImage.getWidth(null);  
        int height_1 = waterImage.getHeight(null);  
        // 设置水印图片的透明度。  
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));  
   
        // 设置水印图片的位置。  
        int widthDiff = width - width_1;  
        int heightDiff = height - height_1;  
        if (x < 0) {  
            x = widthDiff / 2;  
        } else if (x > widthDiff) {  
            x = widthDiff;  
        }  
        if (y < 0) {  
            y = heightDiff / 2;  
        } else if (y > heightDiff) {  
            y = heightDiff;  
        }  
   
        // 将水印图片“画”在原有的图片的制定位置。  
        g.drawImage(waterImage, x, y, width_1, height_1, null);  
        // 关闭画笔。  
        g.dispose();  
   
        // 保存目标图片。  
        ImageIO.write(bufferedImage, ext, file);  
    } 

代码分析:

// 下面两句话,解决 png 透明图片加水印后变黑的问题
bufferedImage = g.getDeviceConfiguration().createCompatibleImage(image.getWidth(null), image.getHeight(null), Transparency.TRANSLUCENT);
g = bufferedImage.createGraphics();

没有这两句代码时,只创建一个空白画布,然后直接绘制 srcImg 、再加水印图片,合成的新图片对透明 png 图片来说,就会产生黑底的问题。

createcompatibleimage 这个方法是核心,它的 API 说明如下:

createcompatibleimage
public bufferedimage createcompatibleimage(int width,
                                           int height,
                                           int transparency)
参数:
width - 返回的 bufferedimage 宽度
height - 返回的 bufferedimage 高度
transparency - 指定的透明度模式

返回:
一个数据布局和颜色模型与此 graphicsconfiguration 兼容且
支持指定透明度的 bufferedimage。

抛出:
illegalargumentexception - 如果透明度不是一个有效值
另请参见:
 transparency.opaque :不透明
 transparency.bitmask:位掩码
 transparency.translucent:透明

返回一个支持指定透明度,并且数据布局和颜色模型与此 graphicsconfiguration 兼容的 bufferedimage。因此加上这句话,就可以解决透明 png 图片变黑的问题。

启示录

为什么 jpg 不会出现这种问题呢?根源是 png 支持透明图,而 jpg 格式不支持透明背景。所以创建支持透明度的画布能解决这个问题。

为什么要为上传的图片添加水印呢?据说当初产品请专业渗透公司进行渗透测试时,对图片上传测出了诸多问题。有一个严重的问题就是在图片中添加可执行的代码片段、可以达到对目标服务器进行攻击的目的。

所以对系统上传的任何图片,都添加透明水印,以拦截恶意代码。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值