Java中对上传的图片获取位置信息并添加位置水印,整合SpringBoot+Idea+腾讯地图Api

430 阅读4分钟

前言

最近工作中有个需求是获取上传的图片的位置信息,并添加位置和时间水印。因为之前也没做过,所以找了很多网上的各种文章,杂七杂八的都有,比较乱,国庆假期正好有时间,写了这篇整理文章,可能写的不好请谅解.

技术使用

本文使用Springboot来做演示项目,因为比较方便和快速,下面我把需要使用到的pom信息贴到代码块中

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>

        <dependency>
            <groupId>com.drewnoakes</groupId>
            <artifactId>metadata-extractor</artifactId>
            <version>2.7.2</version>
        </dependency>
           <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
  1. thymeleaf :用来展示页面和上传图片
  2. fastjson:解析Json数据格式
  3. metadata-extractor :主要用来获取图片中的EXIF信息
  4. commons-fileupload:可忽略,用来把file转换成MultipartFile的依赖

搭建演示Demo

我们需要在templeates中创建2个页面进行显示和上传图片: upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Upload a file for</h1>
<form method="POST" action="/upload" enctype="multipart/form-data">
    <input type="file" name="file" /><br/><br/>
    <input type="submit" value="Submit" />
</form>
</body>
</html>

展示结果页面 result.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Extracted Content:</h1>
<h2>><span  th:text="${text}"></span></h2>
<p>From the image:</p>
<img th:src="'/' + ${file.getName()}"/>
</body>
</html>

处理图片

在开始处理图片之前,我们得准备一张手机拍摄的原图,记住一定得是原图,否则使用drew是获取不到图片的EXIF信息的,如图则为原图(我是直接用手机插PC直接拖的图片):

image.png

ExifUtil:

public class ExifUitl {
    /**
     * 得到图片的exif属性
     */
    public static String[] readExif(File file) throws ImageProcessingException, IOException {
        String[] array = new String[3];
        ImageMetadataReader.readMetadata(file)
                .getDirectories().forEach(v ->
                v.getTags().forEach(t -> {
                     System.out.println(t.getTagName() + " : " + t.getDescription());
                    switch (t.getTagName()) {
                        //                    伟度
                        case "GPS Latitude":
                            array[0] = pointToLatlong(t.getDescription());
                            break;
                        //                        经度
                        case "GPS Longitude":
                            array[1] = pointToLatlong(t.getDescription());
                            break;
                        //                        拍摄时间
                        case "Date/Time Original":
                            array[2] = t.getDescription();
                        default:
                            break;
                    }
                })
        );
        return array;
    }
    /**
     * 经纬度格式  转换
     */
    public static String pointToLatlong (String point ) {
        Double du = Double.parseDouble(point.substring(0, point.indexOf("°")).trim());
        Double fen = Double.parseDouble(point.substring(point.indexOf("°")+1, point.indexOf("'")).trim());
        Double miao = Double.parseDouble(point.substring(point.indexOf("'")+1, point.indexOf("\"")).trim());
        Double duStr = du + fen / 60 + miao / 60 / 60 ;
        return duStr.toString();
    }
  }

controller:

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public RedirectView singleFileUpload(MultipartRequest file, RedirectAttributes redirectAttributes) throws IOException, ImageProcessingException {
        MultipartFile mfile = file.getFile("file");
        File convFile = convert(mfile);
        // 获取图片中得exif信息
        String[] test = ExifUitl.readExif(convFile);  
        for (String one:test) {
            System.out.println(one);
        }   
        return new RedirectView("result");
    }


    @RequestMapping("/result")
    public String result() {
        return "result";
    }

    // 转换成file格式
    public static File convert(MultipartFile file) throws IOException {
        File convFile = new File(file.getOriginalFilename());
        convFile.createNewFile();
        FileOutputStream fos = new FileOutputStream(convFile);
        fos.write(file.getBytes());
        fos.close();
        return convFile;
    }

如何改变文本的样式

然后我们上传图片,页面500是因为没指定File文件,上传完后看看控制台已经打印了图片的EXIF信息,我们主要获取3个key就行了,如果这个地方显示NullPointException则是你的图片问题

image.png

3个Key信息,经纬度已经通过工具类转换,上面已经提供好了.

image.png

下面我们就把获取到的三个key通过水印的方式添加到图片中,现在需要用到一个水印的工具类,如代码块:

public class ImageFileUtil {

    /**
     * 图片添加水印
     *
     * @param srcImgPath
     *            需要添加水印的图片的路径
     * @param outImgPath
     *            添加水印后图片输出路径
     * @param markContentColor
     *            水印文字的颜色
     * @param waterMarkContent
     *            水印的文字
     */
    public File mark(File file, String srcImgPath, String outImgPath, Color markContentColor, String date, String waterMarkContent) {
        Graphics2D g = null;
        try {

            Color color = new Color(133,123,85);
            // 读取原图片信息
            //File srcImgFile = new File(srcImgPath);
            Image srcImg = ImageIO.read(file);
            int srcImgWidth = srcImg.getWidth(null);
            int srcImgHeight = srcImg.getHeight(null);
            // 加水印
            BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
             g = bufImg.createGraphics();
            g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
            // 设置水印的颜色
            g.setColor(markContentColor);
            Font font = new Font("微软雅黑", Font.PLAIN, 77);
            g.setFont(font);

            //设置地址水印的位置坐标
            int x = srcImgWidth - getWatermarkLength(waterMarkContent, g) - 12;
            int y = srcImgHeight - 12;
            g.drawString(waterMarkContent, x, y);

            //设置时间水印的位置坐标
            int xx = srcImgWidth -1* getWatermarkLength(date, g);
            int yy = srcImgHeight - 99;
            g.drawString(date, xx, yy);//画出水印


            // 输出图片到本地
            FileOutputStream outImgStream = new FileOutputStream(outImgPath);
            ImageIO.write(bufImg, "jpg", outImgStream);
            outImgStream.flush();
            outImgStream.close();

            // 输出图片到file中
            File outputfile = new File(file.getName());
            ImageIO.write(bufImg, "png", outputfile);

            return outputfile;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(g!=null){
                // 释放图形资源
                g.dispose();
            }
        }

        return null;
    }

    /**
     * 获取水印文字总长度
     *
     * @param waterMarkContent
     *            水印的文字
     * @param g
     * @return 水印文字总长度
     */
    public int getWatermarkLength(String waterMarkContent, Graphics2D g) {
        return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
    }
}

通过这个工具类我们就能生成水印图片,还有控制层需要添加的测试代码,为之前/upload接口

        File file1 =  new ImageFileUtil().mark(convFile,null,"D:/work/codestory-master/exitdemo/src/main/resources/static/"+mfile.getOriginalFilename(),color,test[2],"测试--后续替换成地址");
        System.out.print("file1:");
        System.out.println(file1);
        redirectAttributes.addFlashAttribute("text","测试");
        redirectAttributes.addFlashAttribute("file", file1);

下面我们来上传图片实测:

image.png

效果基本上实现,水印现在我们已经添加成功了,现在还需要做的就是根据GPS的经纬度得到位置信息文本,然后添加到水印中

结尾

本文的演示代码仅供参考,实际项目中使用变动应该也不大,也算是我在几十篇文章之中吸取的精华,如果对你有丁点儿的帮助的话,点个赞可好,有问题欢迎留言、私信!!