前言
五一放假了,对于分居两地的程序猿们最盼望的是什么?当然是回去家看娃呀!
假期第一天,晚上十点多收拾洗刷完毕,孩子也哄睡着了,媳妇怎么还抱着电脑在沙发上呢?
过去问了一下,原来,手机内存空间被平时的各种照片占满了,要上传到Q私密相册好腾出空间呢,上传时,为了知道记录拍照的时间和地点,正一张张改qq相册的备注呢…
一边修改还一边抱怨,“每次修改100多张,怎么就开始提示系统繁忙了?”,备注改不了就在下面评论上时间和地点!
我汗呀,这种事也只有地主家的傻闺女能干得出来吧,“几千张照片你要上传到啥时候啊?”
“这几天没事干,昨天已经上传过100多张了。”
还好还好,大部分还没上传呢,赶紧收了收马上掉下来的下巴,“别弄了,赶紧睡去吧,明天上午我给你传!”。
五一上午一早就爬起来了,承诺的事情咱必须给办到呀。
对于Ctrl+C,Ctrl+V程序员来说,遇事不决问百度呀,理理思路,搜索一下 Java 怎么获取照片元信息,
简单看几篇文章,整体思路有了,开干!
创建工程,引入依赖
先来创建个 Maven 功能,哎,百度的几个都没有给Jar包或依赖啊,对了,上神器,打开程序员神器,点击检索Jar:
搜索一下要找的 Jar 包,随便点个版本进去,找到下载链接或依赖。
因为创建的Maven工程,直接复制过来依赖:
<dependency>
<groupId>org.lucee</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.lucee</groupId>
<artifactId>xmpcore</artifactId>
<version>5.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
开始编码
新建 Java 类,创建个新方法transDestFileName,读取照片的元信息,如下:
public static String transDestFileName(File file) throws Exception {
Metadata metadata = JpegMetadataReader.readMetadata(file);
String lon = "";
String lat = "";
String picTime = "";
String fileName = file.getName();
System.out.println("开始转换文件:"+fileName);
for(Directory directory : metadata.getDirectories()){
for (Tag tag : directory.getTags()){
if("GPS Latitude".equals(tag.getTagName())){
System.out.println("--获取纬度=="+tag.getDescription());
lon = pointToLatlong(tag.getDescription());
}
if("GPS Longitude".equals(tag.getTagName())){
System.out.println("--获取经度=="+tag.getDescription());
lat = pointToLatlong(tag.getDescription());
}
if("Date/Time".equals(tag.getTagName())){
System.out.println("--拍摄时间=="+tag.getDescription());
picTime = tag.getDescription();
}
}
}
return fileName ;
}
拍摄的时间有了,但是坐标拿到了,怎么转换成文字地址呢?这有个专业名词:全球逆地理编码服务,就是将坐标转换成地址,来吧,上百度地图开放平台api:
文档挺规范的,但是还要获取自己的秘钥,点获取秘钥,又是一顿操作:创建应用,实名认证,过程暂且不表,根据提示操作就行了,最后的最后,终于拿到了秘钥ak。
public static String transAddress(String lon, String lat ) throws Exception {
String ak="换成你自己的秘钥ak";
String urlString="https://api.map.baidu.com/reverse_geocoding/v3/?ak="+ak+"&output=json&coordtype=wgs84ll&location="+lon+","+lat;
String res = "";
URL url = new URL(urlString);
java.net.HttpURLConnection conn = (java.net.HttpURLConnection)url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("GET");
java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream(),"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
res += line+"\n";
}
in.close();
Map map = JSONObject.parseObject(JSONObject.toJSONString(JSONObject.parseObject(res).get("result")), Map.class);
return map.get("formatted_address").toString();
}
需要注意的一点是:从照片中获取到的坐标元信息和接口要求的坐标系属于不同的坐标系,需要转换一下:
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();
}
同样,获取到的照片中的时间格式是这样的:2022:04:27 21:46:17,也转换一下吧:
public static String transDatetime(String datetime) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
Date parse = simpleDateFormat.parse(datetime);
SimpleDateFormat endSpf = new SimpleDateFormat("yyyyMMdd-HHmmss");
String format = endSpf.format(parse);
return format;
}
重新调整 transDestFileName 方法,让它返回日期+地址的文件名称:
public static String transDestFileName(File file) throws Exception {
Metadata metadata = JpegMetadataReader.readMetadata(file);
String lon = "";
String lat = "";
String picTime = "";
String fileName = file.getName();
System.out.println("开始转换文件:"+fileName);
for(Directory directory : metadata.getDirectories()){
for (Tag tag : directory.getTags()){
if("GPS Latitude".equals(tag.getTagName())){
System.out.println("--获取纬度=="+tag.getDescription());
lon = pointToLatlong(tag.getDescription());
}
if("GPS Longitude".equals(tag.getTagName())){
System.out.println("--获取经度=="+tag.getDescription());
lat = pointToLatlong(tag.getDescription());
}
if("Date/Time".equals(tag.getTagName())){
System.out.println("--拍摄时间=="+tag.getDescription());
picTime = tag.getDescription();
}
}
}
if(picTime==null || picTime.equals("")){
throw new Exception();
}
String picAddress = transAddress(lon, lat);
picTime = transDatetime(picTime);
int index = fileName.lastIndexOf(".");
String suffix = fileName.substring(index,fileName.length());
String destPath = picTime+"_"+picAddress+suffix;
System.out.println("--转换后文件名:"+destPath);
return destPath;
}
ok,万事具备,只欠自测,上Main方法:
static String SOURCE_FILE_DIR ="D:\\picture\\source";// 资源文件夹
static String DEST_FILE_DIR ="D:\\picture\\dest";// 目标文件夹
public static void main(String[] args) throws Exception {
File sourceFileDir = new File(SOURCE_FILE_DIR);
File destFileDir = new File(DEST_FILE_DIR);
File[] sourceFiles = sourceFileDir.listFiles();
for(File file : sourceFiles){
try {
Files.copy(file.toPath(), destFileDir.toPath().resolve(transDestFileName(file)), StandardCopyOption.REPLACE_EXISTING);
}catch (Exception e){
System.err.println("--转换错误 :"+file.getName());
}
}
}
好了,代码逻辑就是:把手机中的照片先全部放到电脑source目录中,改名后复制到dest目录,为了单个文件复制异常不影响其他照片,所以 try catch 写到了循环里面,同时记录转换失败的照片。
先拿个照片试一下:
查看目标文件夹,有了!
结尾
看了一眼代码,不多不少100行,满分的欢喜!
赶紧跑去把媳妇叫醒,“去把你手机上的照片传我电脑上!”,正式开启了愉快的五一假期。
附上完整代码:
import com.alibaba.fastjson.JSONObject;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
public class ImageDeal {
static String SOURCE_FILE_DIR ="D:\\picture\\source";// 资源文件夹
static String DEST_FILE_DIR ="D:\\picture\\dest";// 目标文件夹
public static void main(String[] args) throws Exception {
File sourceFileDir = new File(SOURCE_FILE_DIR);
File destFileDir = new File(DEST_FILE_DIR);
File[] sourceFiles = sourceFileDir.listFiles();
for(File file : sourceFiles){
try {
Files.copy(file.toPath(), destFileDir.toPath().resolve(transDestFileName(file)), StandardCopyOption.REPLACE_EXISTING);
}catch (Exception e){
System.err.println("--转换错误 :"+file.getName());
}
}
}
public static String transDestFileName(File file) throws Exception {
Metadata metadata = JpegMetadataReader.readMetadata(file);
String lon = "";
String lat = "";
String picTime = "";
String fileName = file.getName();
System.out.println("开始转换文件:"+fileName);
for(Directory directory : metadata.getDirectories()){
for (Tag tag : directory.getTags()){
if("GPS Latitude".equals(tag.getTagName())){
System.out.println("--获取纬度=="+tag.getDescription());
lon = pointToLatlong(tag.getDescription());
}
if("GPS Longitude".equals(tag.getTagName())){
System.out.println("--获取经度=="+tag.getDescription());
lat = pointToLatlong(tag.getDescription());
}
if("Date/Time".equals(tag.getTagName())){
System.out.println("--拍摄时间=="+tag.getDescription());
picTime = tag.getDescription();
}
}
}
if(picTime==null || picTime.equals("")){
throw new Exception();
}
String picAddress = transAddress(lon, lat);
picTime = transDatetime(picTime);
int index = fileName.lastIndexOf(".");
String suffix = fileName.substring(index,fileName.length());
String destPath = picTime+"_"+picAddress+suffix;
System.out.println("--转换后文件名:"+destPath);
return destPath;
}
public static String transDatetime(String datetime) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
Date parse = simpleDateFormat.parse(datetime);
SimpleDateFormat endSpf = new SimpleDateFormat("yyyyMMdd-HHmmss");
String format = endSpf.format(parse);
return format;
}
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();
}
public static String transAddress(String lon, String lat ) throws Exception {
String ak="换成你自己的秘钥ak";
String urlString="https://api.map.baidu.com/reverse_geocoding/v3/?ak="+ak+"&output=json&coordtype=wgs84ll&location="+lon+","+lat;
String res = "";
URL url = new URL(urlString);
java.net.HttpURLConnection conn = (java.net.HttpURLConnection)url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("GET");
java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream(),"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
res += line+"\n";
}
in.close();
Map map = JSONObject.parseObject(JSONObject.toJSONString(JSONObject.parseObject(res).get("result")), Map.class);
return map.get("formatted_address").toString();
}
}