图片GPS等信息,springboot项目获得图片GPS"/>
java解析图片GPS等信息,springboot项目获得图片GPS
java解析图片GPS等信息
文章目录
- java解析图片GPS等信息
- 1.元数据(Metadata)
- 1.1Metadata概念解释
- 1.2 图片元数据
- 2.代码展示
- 2.1解析图片信息代码实例(有详细注解)
- 2.1springboot后端代码设计
- controller层设计
- service接口
- serviceImpl实现类
- Result设计
- MultipartFile转File
- pom依赖
- postman测试
- 3.参考文献
前言:我在学习的获取图片信息过程中,发现网上的已有的文章要么需要VIP才能查看,要么就是讲的只言片语,注解太少,对于我这种新手很不友好,特此写这篇文章以便他人参考。
本文代码有详细注解,并且在文章末尾附上我所参考的文章,以及一些知识点的补充。
1.元数据(Metadata)
图片的GPS信息存储在图片的元数据中,所以解析图片GPS就是获取图片元数据
1.1Metadata概念解释
元数据(Metadata
),又称中介数据、中继数据,为描述数据的数据(data about data
),主要是描述数据属性(property
)的信息。用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。
元数据是关于数据的组织、数据域及其关系的信息,简言之,元数据就是关于数据的数据。
举个例子:
下面是契诃夫的小说《套中人》中的一段,描写一个叫做瓦莲卡的女子:
(她)年纪已经不轻,三十岁上下,个子高挑,身材匀称,黑黑的眉毛,红红的脸蛋--一句话,不是姑娘,而是果冻,她那样活跃,吵吵嚷嚷,不停地哼着小俄罗斯的抒情歌曲,高声大笑,动不动就发出一连串响亮的笑声:哈,哈,哈!
这段话里提供了这样几个信息:年龄(三十岁上下)、身高(个子高挑)、相貌(身材匀称,黑黑的眉毛,红红的脸蛋)、性格(活跃,吵吵嚷嚷,不停地哼着小俄罗斯的抒情歌曲,高声大笑)。有了这些信息,我们就可以大致想像出瓦莲卡是个什么样的人。推而广之,只要提供这几类的信息,我们也可以推测出其他人的样子。
这个例子中的"年龄"、“身高”、“相貌”、“性格”,就是元数据,因为它们是用来描述具体数据/信息的数据/信息。
当然,这几个元数据用来刻画个人状况还不够精确。我们每个人从小到大,都填过《个人情况登记表》之类的东西吧,其中包括姓名、性别、民族、政治面貌、一寸照片、学历、职称等等…这一套元数据才算比较完备。
1.2 图片元数据
图片元数据(Metadata) 是嵌入到图片文件中的一些标签。比较像文件属性,但是种类繁多。对于数码图像,目前常见的研数据有EXIF, IPTC和XMP
三种:
EXIF
:通常被数码相机在拍摄照片时自动添加,比如相机型号、镜头、曝光、图片尺寸,地理位置等信息IPTF
:比如图片标题、关键字、说明、作者、版权等信息。主要是由人工在后期通过软件写入的数据。XMP
:XMP
实际上是一种元数据存储和管理的标准,可以将Exif,IPTC
或其他的数据都按XMP统一的格式存放在图像文件中。
元数据的嵌入方式因图像格式而异,不同格式的图像文件(如JPG, TIF, DNS
等)有不同的嵌入方式。
2.代码展示
2.1解析图片信息代码实例(有详细注解)
方法封装在自定义的Exif类中,前端获取图片传给后端,后端在service层调用该方法
package com.haoshang.until;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 java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;/*** @author 浩殇* @create 2022/12/7--22:56--周三* Exif*/
public class Exif {//主方法体,封装在Exif类中,通过service层调用public static JSONObject analysis(File file)throws Exception{//将图片传入自定义方法中return printImageTags( file);}private static JSONObject printImageTags(File file) throws ImageProcessingException, IOException {String lat = "";String log = "";
// 读取元数据Metadata metadata = ImageMetadataReader.readMetadata(file);for (Directory directory : metadata.getDirectories()) {
// 打印元数据内容for (Tag tag : directory.getTags()) {String tagName = tag.getTagName(); //标签名String desc = tag.getDescription(); //标签信息
// System.out.println("["+tagName +"]"+ " : " + desc);//照片信息switch (tagName) {// 经度case "GPS Longitude":lat = pointToLatlong(desc);
// System.out.println("***********"+lat+"*************");break;// 纬度case "GPS Latitude":log = pointToLatlong(desc);
// System.out.println("***********"+log+"*************");break;default:break;}}}System.out.println("###########################################################################");String add = getAdd(log, lat);JSONObject jsonObject = JSON.parseObject(add);//controller层设计为传输JSONObject类,读者也可设计为传输地址字符串return jsonObject;/*输出地址String add = getAdd(log, lat);JSONObject jsonObject = JSON.parseObject(add);JSONObject object=jsonObject.getJSONObject("result");String adds = object.getString("formatted_address");*/}/*** 经纬度格式 转换** @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 duStr.toString();}/*** @param log 经度** @param lat 纬度** @return*/public static String getAdd(String log, String lat) {//采用百度地图的接口:String urlString = "="+log+","+lat+"&output=json";System.out.println(urlString);String res = "";try {/*URL定义:统一资源标识符(Uniform Resource Identifier ,URL)是采用一种特定语法标识一个资源的字符串。所标识的资源可能是服务器上的一个文件。Java的URL网络类可以让你通过URL去练级网络服务器并获取资源。*/
// 创建新的urlURL url = new URL(urlString);
// 调用URL对象的openConnection( )来获取HttpURLConnection对象实例:HttpURLConnection conn = (HttpURLConnection) url.openConnection();// 可以使用conn.getOutputStream().write()conn.setDoOutput(true);// 设置HTTP请求使用的方法:GET(不安全) 或者POST,PUT等,HttpURLConnection默认是进行Get请求的:conn.setRequestMethod("GET");
// 调用getInputStream()方法获得服务器返回的输入流,然后输入流进行读取BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));String line;
// 解析获得的数据while ((line = in.readLine()) != null) {res += line + "\n";}in.close();} catch (Exception e) {System.out.println("error in wapaction,and e is " + e.getMessage());}return res;}
}
2.1springboot后端代码设计
springboot上传文件类型为MultipartFile,而解析图片需要上传File文件,所以代码涉及到MultipartFile转为File的类
代码中的MassageMapper, PhotoMassage分别为设计的mapper和实体类pojo,但并未对数据库进行操作,所以并未展示
controller层设计
package com.haoshang.apppro.analysis.controller;import com.alibaba.fastjson.JSONObject;
import com.haoshang.apppro.analysis.service.MassageService;
import com.haoshang.until.Result;
import com.haoshang.until.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/*** @author 浩殇* @create 2022/12/6--19:59--周二* AnalysisController*/
@RestController
@RequestMapping("/analysis")
public class AnalysisController {@Autowiredprivate MassageService massageService;@PostMapping("/photo")private Result<JSONObject> analysisPhoto(@RequestParam("file") MultipartFile file){try {return massageService.analysis(file);} catch (Exception e) {e.printStackTrace();return new Result<>(Status.SYSTEM_ERROR.getCode(), Status.SYSTEM_ERROR.getMessage(),null);}}}
service接口
package com.haoshang.apppro.analysis.service;import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import com.haoshang.apppro.analysis.entity.PhotoMassage;
import com.haoshang.until.Result;
import org.springframework.web.multipart.MultipartFile;
/*** @author 浩殇* @create 2022/12/6--20:19--周二* MassageService*/
public interface MassageService extends IService<PhotoMassage> {Result<JSONObject> analysis(MultipartFile f) throws Exception;
}
serviceImpl实现类
package com.haoshang.apppro.analysis.service.impl;import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haoshang.apppro.analysis.entity.PhotoMassage;
import com.haoshang.apppro.analysis.mapper.MassageMapper;
import com.haoshang.apppro.analysis.service.MassageService;
import com.haoshang.until.Exif;
import com.haoshang.until.Result;
import com.haoshang.until.Shift;
import com.haoshang.until.Status;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/*** @author 浩殇* @create 2022/12/6--20:37--周二* MassageServiceImpl*/
@Service
public class MassageServiceImpl extends ServiceImpl<MassageMapper, PhotoMassage> implements MassageService {@Overridepublic Result<JSONObject> analysis( MultipartFile file) {try {File f=Shift.multipartFileToFile(file);JSONObject jsonObject=Exif.analysis(f);Shift.deleteTempFile(f);//清除缓存return new Result<>(Status.OK.getCode(), "读取图片信息成功",jsonObject);} catch (Exception e) {e.printStackTrace();return new Result<>(Status.SYSTEM_ERROR.getCode(), Status.SYSTEM_ERROR.getMessage(),null);// Status.SYSTEM_ERROR.getCode()是自定义的code,读者采用自己设计的code即可}}
}
Result设计
package com.haoshang.until;import lombok.Data;
/*** @author 浩殇* @create 2022/11/23--20:02--周三* Result*/
@Data
public class Result<T> {// 状态码private int code;// 返回信息private String msg;// 数据private T data;public Result() {}public Result(int code, String msg) {this.code = code;this.msg = msg;}public Result(int code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}public Result(T data) {this.data = data;}}
MultipartFile转File
package com.haoshang.until;import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/*** @author 浩殇* @create 2022/12/8--0:00--周四* shift*/
public class Shift {/*** MultipartFile 转 File** @param file* @throws Exception*/public static File multipartFileToFile(MultipartFile file) throws Exception {File toFile = null;if (file.equals("") || file.getSize() <= 0) {file = null;} else {InputStream ins = null;ins = file.getInputStream();//读入到内存toFile = new File(file.getOriginalFilename());//给File文件实体化inputStreamToFile(ins, toFile);ins.close();}return toFile;}//获取流文件private static void inputStreamToFile(InputStream ins, File file) {try {OutputStream os = new FileOutputStream(file);//以输出流打开file,实现outputStream.write向file中输出内容int bytesRead = 0;byte[] buffer = new byte[8192];while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {os.write(buffer, 0, bytesRead);//文件写入}os.close();ins.close();} catch (Exception e) {e.printStackTrace();}}/*** 删除本地临时文件* @param file*/public static void deleteTempFile(File file) {if (file != null) {File del = new File(file.toURI());del.delete();}}
}
pom依赖
仅展示解析元数据类的依赖
<!--获取元数据依赖--><dependency><groupId>com.drewnoakes</groupId><artifactId>metadata-extractor</artifactId><version>2.7.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.71</version></dependency>
postman测试
3.参考文献
参考文献:
- Java Metadata.getDirectories方法代码示例 - 纯净天空 (vimsky.com)
- 图像元数据(Metadata) 及 Exif信息分析 - 掘金 (juejin.cn)
- (元数据理解)
- JSONArray与JSONObject的区别 - 清华大咖 - 博客园 (cnblogs.com)
- 通过经纬度获取具体省市区位置地址的Api | 程序员磊哥 (examplecode.cn)
更多推荐
java解析图片GPS等信息,springboot项目获得图片GPS
发布评论