Java中如何根据照片定位你的位置

前言

这篇文章很早之前就想写的,奈何本人比较懒 。本周三在朋友圈看到一位名为Lex Python大佬发表过关于这方面的文章,也因为最近思想上有了很深的觉悟,总想记录一些走过的路。与其说些写技术博客,我更倾向于记录知识。

本文没有什么过深的东西,很简单的小知识,适用各行业的同学们。各位看官看看即可,增加一下安全和隐私意识,虽说互联网时代没有隐私可言,但是可以通过一些操作来避免。接下来说一下作死小能手约翰·迈克菲,通过他的故事知道安全和隐私的重要性。

杀毒软件之父“约翰·迈克菲”的传奇人生!

约翰·迈克菲传奇的一生在2021年6月29日落下了帷幕,但是关于他的作死事迹至今还在流传。下面我简略的说一下,详细的情况我们可以找度娘咨询一下。

20世纪60年代末,他在麦卡菲在一家公司中学会了有关早期计算机的基本知识。在38岁之前不务正业,与毒品和酒精为伴。经历过一些打击,迈克菲痛定思痛戒毒,痛改前非,老老实实的当起程序员。

在这个阶段通过他的技术和头脑,发明了全球第一款商用的杀毒软件McAfee,最开始是免费,因为挣不到钱。靠着自己手段和歪招,娶白富美,当上总经理、出任CEO走上人生巅峰。也许生活的太安逸舒适,追求刺激,玩上各种极限危险运动,结果一次意外自己侄子摔死了。

他最有名的一次作死是在2012年——因涉嫌谋杀遭到中美洲小国伯利兹的通缉。 当时迈克菲大爷正在伯利兹一个小镇上当着土皇帝。他买下了一大片土地,在那儿和7个妹子住一起鬼混。不仅如此,他甚至还花钱把当地警察收编成了私人部队。伯利兹*怀疑他在偷偷制造冰毒,曾派特种部队突袭过他家,但却没找到违禁药物。

就在这节骨眼上,迈克菲大爷的邻居被枪杀了。伯利兹*说他有重大杀人嫌疑,而迈克菲大爷则说自己遭到了陷害。 当警方上门逮捕他的时候,迈克菲大爷在后院沙滩上挖了个坑把自己埋进去躲过了搜查。随后他带着女朋友坐小船逃到了邻国危地马拉。

就在这么狼狈的时候,迈克菲大爷也没忘记作死。他不仅发博客嘲笑伯利兹*,还找来了记者和自己一起体验流亡生活。 没想到那记者是个猪队友,他不但把跟迈克菲大爷的合照发到网上,还说“大家快看,迈克菲和我在一起噢!”(一边逃亡一边浪),

网友们一查照片信息,发现里面居然连GPS定位数据都有。数据显示拍照的地点就在危地马拉的一处海边。 警察闻讯赶过去一搜,发现迈克菲大爷果然躲在那里,于是就把他抓了起来。(好家伙,直呼网友内行啊)

Java中如何根据照片定位你的位置

复盘一下约翰·迈克菲如何被抓的?

约翰·迈克菲的故事告诉我们两件事 ,第一件事不作不会死,远离猪队友。第二件事本文正题消除掉Exif信息。

为什么通过一张照片就可以定位到拍照者的准确位置,其实关键在于照片文件里有一个名叫EXIF信息。他其实专门为数码相机照片设计的。用于记录照片的属性和拍摄数据。下面通过代码(技术手段)展示一下EXIF信息!!!

先说配置部分,用Java控制台工程即可,我用的AS控制台工程,建议用IntelliJ IDEA。我是为了省事有现成的编译器就没有用IntelliJ IDEA。我用的是gradle方式作为远程依赖,maven同理。

我熟悉gradle依赖方式,看文档好像metadata-extractor-2.16.0只支持maven,于是把开源项目clone 下来编译成jar放在lib下作为依赖。xmpcore:6.0.6和metadata是配套的需要远程依赖,http请求api方面用的成熟的第三方框架okhttp3。依赖如下图所示:

Java中如何根据照片定位你的位置

package com.example.exif;

import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import java.io.File;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class ReadExifPhoto {

    public static void main(String[] args) {
        File file = new File("/Users/sky/Downloads/IMG_20210706_172959.jpg");
        try {
            Metadata metadata = ImageMetadataReader.readMetadata(file);

            for (Directory directory : metadata.getDirectories()) {

                for (Tag tag : directory.getTags()) {
                    System.out.print(tag.getTagName() + " --> ");
                    System.out.println(tag.getDescription());
                }

                    if (directory.hasErrors()) {
                        for (String error : directory.getErrors()) {
                            System.err.println("ERROR: " + error);
                        }
                    }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



控制台输出信息如下内容,我对关键的信息加上注释。详细的每一条属性需要找一下度娘了,我也记不住属性很多!!!

> Task :lib:ReadExifPhoto.main()
Compression Type --> Baseline
Data Precision --> 8 bits
Image Height --> 3472 pixels    //图片高度 单位(px)
Image Width --> 4624 pixels     //图片宽度 单位(px)
Number of Components --> 3
Component 1 --> Y component: Quantization table 0, Sampling factors 2 horiz/2 vert
Component 2 --> Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert
Component 3 --> Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert
Version --> 1.1
Resolution Units --> none
X Resolution --> 1 dot
Y Resolution --> 1 dot
Thumbnail Width Pixels --> 0
Thumbnail Height Pixels --> 0
Image Width --> 4624 pixels
Model --> Redmi Note 8 Pro   //拍摄手机型号
Image Height --> 3472 pixels
Orientation --> Right side, top (Rotate 90 CW)
Date/Time --> 2021:07:06 17:30:00     //拍摄照片时间
Make --> Xiaomi                        //拍摄手机厂商 ,小米手机
F-Number --> f/1.9
Focal Length --> 5.4 mm
Exposure Time --> 9999/500000 sec
Flash --> Flash did not fire
Unknown tag (0x9999) --> {"mirror":false,"sensor_type":"rear","Hdr":"auto","OpMode":36869}
ISO Speed Ratings --> 396
Unknown tag (0x8895) --> 0
Exif Image Height --> 3472 pixels
Exif Image Width --> 4624 pixels
Aperture Value --> f/1.9
Shutter Speed Value --> 49.5 sec
Sub-Sec Time --> 549
GPS Latitude Ref --> N                //北纬
GPS Latitude --> 39° 58' 31.89"    //纬度
GPS Longitude Ref --> E              //东经
GPS Longitude --> 116° 20' 42.77"  //经度
GPS Time-Stamp --> 09:29:59.000 UTC
GPS Date Stamp --> 2021:07:06
XMP Value Count --> 1
Number of Tables --> 4 Huffman tables
Detected File Type Name --> JPEG     //图片类型
Detected File Type Long Name --> Joint Photographic Experts Group
Detected MIME Type --> image/jpeg
Expected File Name Extension --> jpg
File Name --> IMG_20210706_172959.jpg    //拍摄图片文件名
File Size --> 6556702 bytes         //图片文件大小  转换一下大约6.6mb
File Modified Date --> 星期二 七月 06 17:30:46 +08:00 2021

从上面可以获取到关键一点GPS定位信息,从控制台得知E116° 20’ 42.77,N 39° 58’ 31.89, 这个信息是不能直接被使用,需要进行一次转换才能找到真正位置信息。

在数学中,表示角度的度、分、秒分别使用°、′、″符号进行表示。1°=60′,1′=60″ ,1°=3600″。下面的转换工具代码如下:


    /**
     * 经纬度坐标格式转换
     * @param Gps
     */
    private static double conversionUtil(String Gps) {
        String du = Gps.split("°")[0].replace(" ", "");
        String fen = Gps.split("°")[1].split("'")[0].replace(" ", "");
        String miao = Gps.split("°")[1].split("'")[1].replace(" ", "").replace("\"", "");
        return Double.parseDouble(du)+Double.parseDouble(fen)/60 + Double.parseDouble(miao)/3600;
    }

 System.out.println("得到的纬度数据为:====="+conversionUtil("39° 58' 31.89"));
 System.out.println("得到的经度数据为:====="+conversionUtil("116° 20' 42.77"));

Console  log  输出:

得到的纬度数据为:=====39.975525000000005
得到的经度数据为:=====116.34521388888888

得到了真实的gps定位地址,接下来应该去百度地图或者高德地图找到详细的地理信息。这里我才用的是高德开放平台逆地理web api方式 ,详细调用高德api 需要看下文档,文档内容如下:地理/逆地理编码-API文档-开发指南-Web服务 API | 高德地图API


//演示代码如下,高德key是我自己申请的 ,大家拿去用真实有效。
//也可以用自己申请后的参数。请求上限每天600次,谨慎使用

private  static  void  sendGetRequest(){
       String url = "http://restapi.amap.com/v3/geocode/regeo?key=adbda67c40bbe332e1d18b5ddb03d721&location=116.345214,39.975525&radius=500&extensions=base&batch=false&roadlevel=1";

       OkHttpClient okHttpClient = new OkHttpClient();
       final Request request = new Request.Builder()
               .url(url)
               .get()//默认就是GET请求,可以不写
               .build();
       Call call = okHttpClient.newCall(request);
       call.enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException exception) {
               System.err.println(exception);
           }

           @Override
           public void onResponse(Call call, Response response) throws IOException {
               System.out.println( "onResponse: " + response.body().string());
           }
       });

   }

Java中如何根据照片定位你的位置

formatted_address字段就是根据gps得到有效地址信息了。因为计算方式略有不同,我的真实的地址和高德给的略有差异。所以你还是找不到我(手动滑稽)!!!! 代码内容到此结束


{
	"status": "1",
	"regeocode": {
		"addressComponent": {
			"city": [],
			"province": "北京市",
			"adcode": "110108",
			"district": "海淀区",
			"towncode": "110108008000",
			"streetNumber": {
				"number": "22号",
				"location": "116.344753,39.975785",
				"direction": "西北",
				"distance": "48.7649",
				"street": "知春路"
			},
			"country": "中国",
			"township": "北太平庄街道",
			"businessAreas": [{
				"location": "116.339877,39.965569",
				"name": "大钟寺村",
				"id": "110108"
			}],
			"building": {
				"name": [],
				"type": []
			},
			"neighborhood": {
				"name": [],
				"type": []
			},
			"citycode": "010"
		},
		"formatted_address": "北京市海淀区北太平庄街道知春路22号知春路22号院"
	},
	"info": "OK",
	"infocode": "10000"
}

你的位置是怎么泄露的呢?

满足以下三个条件就会泄露位置:

  • 打开手机GPS定位

  • 拍照设置成保存地理位置

  • 拍照后发送原图。因此如果我们对症下药注意这几个方面,照片的信息安全会大大提高。

如何防范这个问题呢

只要对图片经过压缩,用PS修改等操作后,该图片的EXIF信息可能就不存在了。朋友圈我们经常用,一般情况下腾讯会对微信朋友圈、公众号的推文里的图片进行压缩,保护个人隐私。建议朋友圈不要发原图!!! 小姐姐们一点也不担心因为你们都是美美的,完全不用担心图片exif信息泄露。

  • 安卓系统:以小米手机为例,进入相机设置–关闭保存地理位置信息(开口一句国粹,好像我的保存地理位置信息一直都没关过,我好方 (~ ̄(OO) ̄)ブ)

Java中如何根据照片定位你的位置

  • 苹果IOS系统:隐私权–定位服务–相机(永不)

Java中如何根据照片定位你的位置

提醒

  • 不认识的人坚决不提供任何缘由的高清原图,防止获取你的位置信息

  • 发布教程、攻略、贴吧的图片应稍加处理再发布

  • 不对我国军事等敏感信息等以任何形势提供拍摄(例如铁路线上运送军事武器等)

结尾

有一次妹子问我,你怎么知道我在那个位置 ,现在你应该知道了吧 ,希望你看不到!!!

上一篇:如何用python获取照片的拍摄地


下一篇:java设计模式之 单例模式 Singleton