Java 手机上传图片打水印时,图片被翻转90度

问题背景:

手机端上传图片,后台打水印时,发现图片被翻转90度,window的图片上传确展示正常。

解决办法:

1. 引用依赖:

<dependency>
   <groupId>com.drewnoakes</groupId>
   <artifactId>metadata-extractor</artifactId>
   <version>2.7.2</version>
</dependency>

2. 获取图片翻转角度,并纠正后打水印,上传至文件服务器

/**
 * 获取图片旋转角度(手机端上传的照片
 *
 * @param inputStream 图片流
 * @return
 */
private static int getAngle(InputStream inputStream) throws Exception {
    Metadata metadata = ImageMetadataReader.readMetadata(inputStream);
    for (Directory directory : metadata.getDirectories()) {
        for (Tag tag : directory.getTags()) {
            if ("Orientation".equals(tag.getTagName())) {
                String orientation = tag.getDescription();
                if (orientation.contains("90")) {
                    return 90;
                } else if (orientation.contains("180")) {
                    return 180;
                } else if (orientation.contains("270")) {
                    return 270;
                }
            }
        }
    }
    return 0;
} 
@Override
public String setWaterMarkAndUpload(MultipartFile file, String type, String address) throws IOException, NoSuchAlgorithmException {
    if (ObjectUtils.isEmpty(type)) {
        throw new BusinessException("类型不能为空!");
    }

    // 获取redis水印配置信息
    String key = RedisKeyConstant.wx_citizen_watermark_key + ":" + type;
    WxCitizenSettingWatermarkVO settingWatermarkVO = BeanUtil.toBean(JSONUtil.parse(redisService.get(key)), WxCitizenSettingWatermarkVO.class);
    if (ObjectUtils.isEmpty(settingWatermarkVO)) {
        throw new BusinessException("未找到系统的水印设置!");
    }
    if (settingWatermarkVO.getDbStatus().equals("2")) {
        String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
        String url = this.upload(file, fileName, "1", null);
        return url;
    }

    if (ObjectUtil.isNotEmpty(settingWatermarkVO.getWmWord()) && settingWatermarkVO.getWmWord().equals("1")) {
        if (ObjectUtil.isEmpty(address)) {
            throw new BusinessException("地址不能为空!");
        }
    }

    BufferedImage bufImg = null;
    try {
        //文件全名+后缀
        String originalFileName = file.getOriginalFilename();
        String fileName = originalFileName.substring(0, originalFileName.lastIndexOf("."));
        //后缀名
        String suffix = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
        //判断文件类型是否为图片
        String[] imgType = {"bmp", "jpg", "jpeg", "png", "gif"};
        List<String> stringList = Arrays.asList(imgType);
        if (!stringList.contains(suffix.toLowerCase())) {
            throw new BusinessException("文件类型错误,请选择图片类型的文件");
        }
        //数据流转换存储(防止流失效)
        InputStream inputStream = file.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 1024];
        int len;
        while ((len = inputStream.read(buffer)) > -1) {
            baos.write(buffer, 0, len);
        }
        baos.flush();
        // 获取图片旋转角度
        InputStream metadataInputStream = new ByteArrayInputStream(baos.toByteArray());
        int angle = getAngle(metadataInputStream);


        //获取源图片字节(供未开启水印时使用)
        byte[] imgBuffer = baos.toByteArray();
        InputStream stream = new ByteArrayInputStream(baos.toByteArray());
        //获取源图片(供开启水印时使用)
        Image srcImg = ImageIO.read(stream);

        // 是否错图需要纠正
        boolean isCorrectImg = false;
        //源图片宽高
        int imgWidth = 0;
        int imgHeight = 0;
        if (angle == 90 || angle == 270) {
            isCorrectImg = true;
            imgWidth = srcImg.getHeight(null);
            imgHeight = srcImg.getWidth(null);
        } else {
            isCorrectImg = false;
            imgWidth = srcImg.getWidth(null);
            imgHeight = srcImg.getHeight(null);
        }

        //  水平偏移量
        int devLevel = 0;
        //  垂直偏移量
        int devVertical = 0;
        //  水印透明度
        float alpha = 1f;
        //  水印横向位置
        int positionWidth = 10;
        //  水印纵向位置
        int positionHeight = 20;
        //  水印文字颜色
        String colorText = settingWatermarkVO.getColor();
        final String[] colors = colorText.replace("rgb", "").replace("(", "").replace(")", "").replace(" ", "").split(",");
        List<String> strings = Arrays.asList(colors);
        List<Integer> collect = strings.stream().map(p -> Integer.valueOf(p)).collect(Collectors.toList());
        Color color = new Color(collect.get(0), collect.get(1), collect.get(2));

        //格式化时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = simpleDateFormat.format(new Date());
        //要打印的文字
        List<String> texts = new ArrayList<>();
        //水印文字
        switch (settingWatermarkVO.getWmWord()) {
            case "1": // 地址+时间
                // 去掉地址忽略内容
                if (ObjectUtil.isNotEmpty(settingWatermarkVO.getAddressIgnoreText())) {
                    address = address.replace(settingWatermarkVO.getAddressIgnoreText(), "");
                }
                texts.add(address);
                texts.add(time);
                break;
            case "2": // 时间
                texts.add(time);
                break;
            default:
                throw new BusinessException("文字类型错误");
        }
        bufImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
        //  水印文字字体
        Font font = new Font("宋体", Font.BOLD, settingWatermarkVO.getSize());
        //加文字水印
        mark(bufImg, srcImg, texts, font, color, imgWidth, imgHeight, settingWatermarkVO.getImgPosition(), isCorrectImg, angle);
        //图片流转字节流
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(bufImg, suffix, out);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(out.toByteArray());
        out.close();
        //上传至文件服务器
        String url = this.upload(byteArrayInputStream, file.getContentType(), suffix, fileName, "0");
        System.out.println("url:"+url);
        return url;
    } catch (Exception ex) {
        ex.printStackTrace();
        throw new BusinessException(ex.getMessage());
    } finally {
        if (bufImg != null) {
            bufImg.getGraphics().dispose();
        }
    }

}
/**
 * 加文字水印
 *  @param bufImg
 * @param img
 * @param texts
 * @param font
 * @param color
 * @param x
 * @param y
 */
public static void mark(BufferedImage bufImg, Image img, List<String> texts, Font font, Color color, int x, int y, String position, boolean isCorrectImg, int angle) {
    Graphics2D g = bufImg.createGraphics();
    // 创建一个FontMetrics对象
    FontMetrics fm = g.getFontMetrics(font);
    int textHeight = fm.getHeight();
    // 查找水印中字符最长
    String longest = texts.stream()
            .max(Comparator.comparingInt(String::length))
            .orElse("");
    int textWidth = fm.stringWidth(longest);
    int positionWidth = textWidth;
    int positionHeight = textHeight;
    //设置要打印的位置
    switch (position) {
        case "左上":
            positionWidth = 0;
            positionHeight = 0 + textHeight;
            break;
        case "右上":
            positionWidth = x - textWidth;
            positionHeight = textHeight;
            break;
        case "中上":
            positionWidth = (x - textWidth) / 2;
            positionHeight = textHeight;
            break;
        case "正中":
            positionWidth = (x - textWidth) / 2;
            positionHeight = (y + textHeight) / 2;
            break;
        case "左中":
            positionWidth = 0;
            positionHeight = (y + textHeight) / 2;
            break;
        case "右中":
            positionWidth = x - textWidth;
            positionHeight = (y + textHeight) / 2;
            break;
        case "左下":
            positionWidth = 0;
            positionHeight = y - textHeight;
            break;
        case "中下":
            positionWidth = (x - textWidth) / 2;
            positionHeight = y - textHeight;
            break;
        case "右下":
            positionWidth = x - textWidth;
            positionHeight = y - textHeight;
            break;
        default:
            positionWidth = x - textWidth;
            positionHeight = y - textHeight;
            break;
    }

    // 手机端上传的图片存在翻转问题时,纠正位置
    if (isCorrectImg) {
        // 中心点位置
        double centerWidth = ((double) x) / 2;
        double centerHeight = ((double) y) / 2;
        g.rotate(Math.toRadians(angle), centerWidth, centerHeight);
        g.drawImage(img, (x - img.getWidth(null)) / 2, (y-img.getHeight(null)) / 2, null);
        g.rotate(Math.toRadians(-angle), centerWidth, centerHeight);
    } else {
        g.drawImage(img, 0, 0, bufImg.getWidth(), bufImg.getHeight(), null);
    }
    g.setColor(color);
    g.setFont(font);
    g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1f));

    int size = font.getSize();
    for (int i = 0; i < texts.size(); i++) {
        g.drawString(texts.get(i), positionWidth, positionHeight-size*i);
    }
    g.dispose();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Java中的MultipartFile图片上传而言,可以通过以下步骤实现图片水印的功能: 1. 首先,你需要在前端实现图片的上传功能,将图片以MultipartFile类型的对象传输到后端的controller层。 2. 在controller层接收到图片后,可以使用第三方库,如ImageIO或Thumbnails,来对图片进行处理。这些库提供了丰富的方法来处理图片。 3. 为了给图片添加水印信息,你可以创建一个水印图片,或者使用文字作为水印。要创建水印图片,你可以使用Java的Graphics2D类,在水印图片上绘制所需的文字或图形。 4. 一旦你有了水印图片,你可以使用Graphics2D的drawImage方法将水印图片绘制到原始图片上。可以通过指定位置和大小来调整水印的位置和大小。 5. 绘制水印后,你可以将带有水印图片保存到指定的目录或返回给前端。 总结一下,要实现Java MultipartFile图片上传加水印的功能,你需要在controller层接收到前端上传的图片,使用第三方库对图片进行处理,并在图片上绘制水印。最后,保存带有水印图片或返回给前端。<span class="em">1</span> #### 引用[.reference_title] - *1* [Java MultipartFile实现文件上传并为图片加上水印(二)](https://blog.csdn.net/weixin_42171478/article/details/113735313)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值