问题背景:
手机端上传图片,后台打水印时,发现图片被翻转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(); }