通过Java代码获取图片拍摄详细地址和其他相关信息

1 准备工作

1.1 导入依赖

        <!-- 地址定位 -->
        <dependency>
            <groupId>com.drewnoakes</groupId>
            <artifactId>metadata-extractor</artifactId>
            <version>2.16.0</version>
        </dependency>

1.2 照片图片

图片名和地址如上所述

2 具体实现

2.1 图片工具类ImageUtils.java

package com.wkf.workrecord.utils;

import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.UUID;

/**
 * 图片工具类
 * @author wuKeFan
 * @date 2022-08-30 15:16:07
 */
@Slf4j
public class ImageUtils {

    public static void main(String[] args) throws Exception {
        ArrayList<String> list = new ArrayList<>();
        list.add("C:\\Users\\1\\Desktop\\rentou.jpg");
        list.add("C:\\Users\\1\\Desktop\\rentou.jpg");
        String path="D://1";
        String resultPath = doVImageMerging2(list, path);
        System.out.println(resultPath);
    }

    //2张图片水平合并
    public static String doVImageMerging2(ArrayList<String> list, String pathFile) {
        // 读取待合并的文件
        BufferedImage bi1 = null;
        BufferedImage bi2 = null;
        // 调用mergeImage方法获得合并后的图像
        BufferedImage destImg = null;
        try {
            bi2 = getBufferedImage(list.get(0));
            bi1 = getBufferedImage(list.get(1));
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 调用mergeImage方法获得合并后的图像
        try {
            if (bi1 != null && bi2 != null) {
                destImg = mergeImage(bi1, bi2, false);//true为水平,false为垂直
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 保存图像
        String savePath = pathFile + "/";//文件存放路径
        String fileName = createImageName() + ".jpg";//文件名称
        String fileType = "jpg";//文件格式
        boolean result = saveImage(destImg, savePath, fileName, fileType);
        log.info("图片保存结果:{}", result ? "成功" : "失败");
        return savePath + fileName;
    }
    public static BufferedImage getBufferedImage(String fileUrl) throws IOException {
        File f = new File(fileUrl);
        return ImageIO.read(f);
    }
    /**
     * 待合并的两张图必须满足这样的前提,如果水平方向合并,则高度必须相等;如果是垂直方向合并,宽度必须相等。
     * mergeImage方法不做判断,自己判断。
     *
     * @param img1
     * 待合并的第一张图
     * @param img2
     * 带合并的第二张图
     * @param isHorizontal
     * 为true时表示水平方向合并,为false时表示垂直方向合并
     * @return 返回合并后的BufferedImage对象
     */
    public static BufferedImage mergeImage(BufferedImage img1, BufferedImage img2, boolean isHorizontal) throws IOException {
        int w1 = img1.getWidth();
        int h1 = img1.getHeight();
        int w2 = img2.getWidth();
        int h2 = img2.getHeight();
        // 从图片中读取RGB
        int[] ImageArrayOne = new int[w1 * h1];
        ImageArrayOne = img1.getRGB(0, 0, w1, h1, ImageArrayOne, 0, w1); // 逐行扫描图像中各个像素的RGB到数组中
        int[] ImageArrayTwo = new int[w2 * h2];
        ImageArrayTwo = img2.getRGB(0, 0, w2, h2, ImageArrayTwo, 0, w2);
        // 生成新图片
        BufferedImage DestImage;
        // 水平方向合并
        if (isHorizontal) {
        // DestImage = new BufferedImage(w1+w2, h1, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d;
            if (h1 >= h2) {
                DestImage = new BufferedImage(w1 + w2, h1, BufferedImage.TYPE_INT_RGB);
            } else {
                DestImage = new BufferedImage(w2, h1, BufferedImage.TYPE_INT_RGB);//TYPE_INT_RGB
            }
            g2d = DestImage.createGraphics();
            g2d.setPaint(Color.WHITE);
            g2d.fillRect(0, 0, w1 + w2, h1);
            g2d.dispose();
            DestImage.setRGB(0, 0, w1, h1, ImageArrayOne, 0, w1); // 设置上半部分或左半部分的RGB
            DestImage.setRGB(w1, 0, w2, h2, ImageArrayTwo, 0, w2);
        } else { // 垂直方向合并
            Graphics2D g2d;
            if (w1 >= w2) {
                DestImage = new BufferedImage(w1, h1 + h2, BufferedImage.TYPE_INT_RGB);//TYPE_INT_RGB
            } else {
                DestImage = new BufferedImage(w2, h1 + h2, BufferedImage.TYPE_INT_RGB);//TYPE_INT_RGB
            }
            g2d = DestImage.createGraphics();
            g2d.setPaint(Color.WHITE);
            g2d.fillRect(0, 0, w1 + w2, h1 + h2);
            g2d.dispose();
            DestImage.setRGB(0, 0, w1, h1, ImageArrayOne, 0, w1); // 设置上半部分或左半部分的RGB
            DestImage.setRGB(0, h1, w2, h2, ImageArrayTwo, 0, w2); // 设置下半部分的RGB
        }
        return DestImage;
    }
    //随机生成图片名称
    public static String createImageName() {
        // 创建 GUID 对象
        UUID uuid = UUID.randomUUID();
        // 得到对象产生的ID
        String a = uuid.toString();
        // 转换为大写
        a = a.toUpperCase();
        // 替换 -
        a = a.replaceAll("-", "");
        return a;
    }
    public static boolean saveImage(BufferedImage savedImg, String saveDir, String fileName, String format) {
        boolean flag = false;
        // 先检查保存的图片格式是否正确
        String[] legalFormats = { "jpg", "JPG", "png", "PNG", "bmp", "BMP" };
        int i;
        for (i = 0; i < legalFormats.length; i++) {
            if (format.equals(legalFormats[i])) {
                break;
            }
        }
        if (i == legalFormats.length) { // 图片格式不支持
            System.out.println("不是保存所支持的图片格式!");
            return false;
        }
        // 再检查文件后缀和保存的格式是否一致
        String postfix = fileName.substring(fileName.lastIndexOf('.') + 1);
        if (!postfix.equalsIgnoreCase(format)) {
            System.out.println("待保存文件后缀和保存的格式不一致!");
            return false;
        }
        String fileUrl = saveDir + fileName;
        File file = new File(fileUrl);
        try {
            flag = ImageIO.write(savedImg, format, file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 经纬度格式  转换为  度分秒格式 ,如果需要的话可以调用该方法进行转换
     *
     * @param point 坐标点
     * @return 返回时分秒格式
     */
    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 Double.toString(duStr);
    }

    /***
     * 经纬度坐标格式转换(* °转十进制格式)
     * @param gps gps坐标
     */
    public static double latLng2Decimal(String gps) {
        String a = gps.split("°")[0].replace(" ", "");
        String b = gps.split("°")[1].split("'")[0].replace(" ", "");
        String c = gps.split("°")[1].split("'")[1].replace(" ", "").replace("\"", "");
        return Double.parseDouble(a) + Double.parseDouble(b) / 60 + Double.parseDouble(c) / 60 / 60;
    }

}

2.2 地址处理工具类(主要逻辑代码都在这个类)ImageAddressUtils.java

package com.wkf.workrecord.tools.photo.address.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import com.wkf.workrecord.tools.baidu.map.BaiduMapConstants;
import com.wkf.workrecord.tools.photo.address.entity.AddressComponentResponse;
import com.wkf.workrecord.tools.photo.address.entity.ReverseGeocodingRequest;
import com.wkf.workrecord.utils.ImageUtils;
import com.wkf.workrecord.utils.ObjectUtils;
import com.wkf.workrecord.utils.fuying.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;

/**
 * 图片地址工具类
 * @author wuKeFan
 * @date 2023-02-24 15:44:31
 */
@Slf4j
@Component
public class ImageAddressUtils {

    @Resource
    private RestTemplate restTemplate;

    public String getPhotoAddress(String fileName) throws ImageProcessingException, IOException {
        File file = new File(fileName);
        Metadata metadata = ImageMetadataReader.readMetadata(file);
        //图片的所有目录信息
        Iterable<Directory> directories = metadata.getDirectories();
        for (Directory directory : directories) {
            for (Tag tag : directory.getTags()) {
                log.info("[{}] - {} = {}",
                        directory.getName(), tag.getTagName(), tag.getDescription());
            }
            if (directory.hasErrors()) {
                for (String error : directory.getErrors()) {
                    log.info("ERROR: {}", error);
                }
            }
        }
        Double lat = null;
        Double lng = null;
        for (Directory directory : directories) {
            for (Tag tag : directory.getTags()) {
                String tagName = tag.getTagName();  //标签名
                String desc = tag.getDescription(); //标签信息
                switch (tagName) {
                    case "Image Height":
                        log.info("图片高度: " + desc);
                        break;
                    case "Image Width":
                        log.info("图片宽度: " + desc);
                        break;
                    case "Date/Time Original":
                        log.info("拍摄时间: " + desc);
                        break;
                    case "GPS Latitude":
                        log.info("纬度 : " + desc);
                        log.info("纬度(度分秒格式) : " + ImageUtils.pointToLatLong(desc));
                        lat = ImageUtils.latLng2Decimal(desc);
                        break;
                    case "GPS Longitude":
                        log.info("经度: " + desc);
                        log.info("经度(度分秒格式): " + ImageUtils.pointToLatLong(desc));
                        lng = ImageUtils.latLng2Decimal(desc);
                        break;
                }
            }
        }
        //经纬度转地主使用百度api
        if (lat != null && lng != null) {
            ReverseGeocodingRequest request = ReverseGeocodingRequest.builder()
                    .token(BaiduMapConstants.BAIDU_MAP_TOKEN).output(BaiduMapConstants.OUTPUT_TYPE_JSON)
                    .coordinateType(BaiduMapConstants.COORDINATE_TYPE_GPS).lat(lat).lng(lng)
                    .build();
            Map<String, Object> map = ObjectUtils.objectToMap(request);
            String result = restTemplate.getForObject(BaiduMapConstants.REVERSE_GEOCODING_URL, String.class, map);
            JSONObject resultObj = JSONObject.parseObject(RegexUtils.underlineToHump(result));
            if (resultObj != null && Objects.equals(resultObj.getInteger("status"), 0)) {
                AddressComponentResponse response = resultObj.getJSONObject("result").getObject("addressComponent", AddressComponentResponse.class);
                return "拍摄地点:" + response.getCountry() + " " + response.getProvince() + " " + response.getCity() + " " + response.getDirection() + " "
                        + response.getStreet() + " " + resultObj.getJSONObject("result").get("formattedAddress") + " " + resultObj.getJSONObject("result").get("business");
            }
        }
        return null;
    }

}

2.3 实体类AddressComponentResponse.java和ReverseGeocodingRequest.java

package com.wkf.workrecord.tools.photo.address.entity;

import lombok.Data;

/**
 * 地址构成实体类
 * @author wuKeFan
 * @date 2022-08-18 14:48:28
 */
@Data
public class AddressComponentResponse {

    /**
     * 国家
     */
    private String country;

    /**
     * 国家编码
     */
    private int countryCode;

    /**
     * 国家英文缩写(三位)
     */
    private String countryCodeIso;

    /**
     * 国家英文缩写(两位)
     */
    private String countryCodeIso2;

    /**
     * 省名
     */
    private String province;

    /**
     * 城市名
     */
    private String city;

    /**
     * 城市所在级别(仅国外有参考意义。
     * 国外行政区划与中国有差异,城市对应的层级不一定为『city』。
     * country、province、city、district、town分别对应0-4级,
     * 若city_level=3,则district层级为该国家的city层级)
     */
    private int cityLevel;

    /**
     * 区县名
     */
    private String district;

    /**
     * 乡镇名,需设置extensions_town=true时才会返回
     */
    private String town;

    /**
     * 乡镇id
     */
    private String townCode;

    /**
     * 相对当前坐标点的距离,
     * 当有门牌号的时候返回数据
     */
    private String distance;

    /**
     * 和当前坐标点的方向
     */
    private String direction;

    /**
     * 行政区划代码
     */
    private String adcode;

    /**
     * 街道名(行政区划中的街道层级)
     */
    private String street;

    /**
     * 街道门牌号
     */
    private String streetNumber;

}
package com.wkf.workrecord.tools.photo.address.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 逆地理编码参数请求实体类
 * @author wuKeFan
 * @date 2022-08-18 14:29:15
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReverseGeocodingRequest {

    /**
     * 用户申请注册的key,即ak
     */
    private String token;

    /**
     * 输出格式为json或者xml
     */
    private String output;

    /**
     * 坐标的类型,
     * 目前支持的坐标类型包括:bd09ll(百度经纬度坐标)、
     * bd09mc(百度米制坐标)、gcj02ll(国测局经纬度坐标,仅限中国)、wgs84ll( GPS经纬度)
     */
    private String coordinateType;

    /**
     * 纬度
     */
    private Double lat;

    /**
     * 经度
     */
    private Double lng;

}

2.4 测试代码

package com.wkf.workrecord.study;

import com.drew.imaging.ImageProcessingException;
import com.wkf.workrecord.tools.photo.address.utils.ImageAddressUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.io.IOException;

/**
 * 公共测试类
 * @author wuKeFan
 * @date 2023-02-20 09:53:14
 */
@Slf4j
@SpringBootTest
public class PublicTest {

    @Resource
    private ImageAddressUtils imageAddressUtils;

    @Test
    public void photoAddressTest() throws ImageProcessingException, IOException {
        System.out.println(imageAddressUtils.getPhotoAddress("C:\\Users\\1\\Desktop\\2023_02_18_20_21_IMG_0057.JPG"));
    }

}

运行结果如图:

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴名氏.

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值