服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - JAVA使用Ip2region获取IP定位信息的操作方法

JAVA使用Ip2region获取IP定位信息的操作方法

2022-12-29 16:16喵喵扑 Java教程

这篇文章主要介绍了JAVA使用Ip2region获取IP定位信息,ip2region - 是国内开发者开发的离线IP地址定位库,针对国内IP效果较好,国外的部分IP只能显示国家,对java获取IP定位信息操作过程感兴趣的朋友一起看看吧

ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询

ip2region - 是国内开发者开发的离线IP地址定位库,针对国内IP效果较好,国外的部分IP只能显示国家。

项目gitee地址:

?
1
https://gitee.com/lionsoul/ip2region.git

先安装依赖

?
1
2
3
4
5
<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>1.7.2</version>
</dependency>

下载离线IP定位库

离线数据库在项目的data文件夹下,名称为ip2region.db,其他2个文件是用于生成离线库的,可不用下载。

?
1
https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region.db

下载到离线数据库后,我们需要读取这个数据库,我们可以放在项目的resources目录,但是我不建议这样做,这样打包的jar会变得很大,部署时麻烦。

我们指定一个绝对路径,这个部署的时候也不用每次上传,这个数据库一般不会修改,如果数据库更新了,单独上传即可。

因为我们项目使用阿里云oss,服务器也在阿里云,所以我就把数据库存到oss中,阿里云内网读取还是很快的,这样免得我修改地址,更新数据库我也只需要在本地上传到oss即可。

下面我们定义类封装ip2region

  • 记录映射实体 IpInfo.java

该类用于接受解析后的数据,我们也可以使用map来接收,我这里就使用模型来组装数据。

类使用 lombok ,如果不喜欢的自行修改为普通类。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import lombok.Data;
/**
 * 域名信息.
 *
 * @author https://www.cnblogs.com/lixingwu
 * @date 2022-05-24 15:07:47
 */
@Data
public class IpInfo {
    /*** 国家 */
    private String country;
    /*** 地区 */
    private String region;
    /*** 省 */
    private String province;
    /*** 市 */
    private String city;
    /*** 运营商 */
    private String isp;
}

ip解析工具类 Ip2regionAnalysis.java

该类主要用于加载数据库和解析IP信息,然后把查询的结果组装为IpInfo的;

这个类我使用了使用单例模式(双重校验锁DCL)进行编写,在构造函数里加载IP数据库,这样数据库就只会加载一遍。

类中还用到了工具包hutool,需要自行引入,具体操作自行百度。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.yunding.vote.domain.IpInfo;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbMakerConfigException;
import org.lionsoul.ip2region.DbSearcher;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Optional;
/**
 * 使用单例模式(双重校验锁DCL)
 *
 * @author https://www.cnblogs.com/lixingwu
 * @date 2022-05-24 10:45:42
 */
@Slf4j
public class Ip2regionAnalysis {
    private volatile static Ip2regionAnalysis analysis;
    /**
     * TODO 这个数据库地址,改成自己的,不要用这地址啊,这个需要登录才能下载
     * ip数据库地址
     */
    public static final String IP_REGION_DB_URL = "https://gitee.com/lionsoul/ip2region/raw/master/data/ip2region.db";
    /**
     * ip数据库字节
     */
    private final byte[] dbBinStr;
    /**
     * 初始化,下载ip数据库文件转为为文件输出流
     */
    private Ip2regionAnalysis() {
        // 下载IP数据库文件
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        HttpUtil.download(IP_REGION_DB_URL, outputStream, false, new StreamProgress() {
            @Override
            public void start() {
                log.info("下载IP数据库文件...");
            }
            @Override
            public void progress(long progressSize) {
            }
            @Override
            public void finish() {
                double fileSize = NumberUtil.div(outputStream.size(), (1024 * 1024), 2);
                log.info("IP数据库文件下载成功,数据库文件大小[{}MB]", fileSize);
            }
        });
        dbBinStr = outputStream.toByteArray();
        IoUtil.close(outputStream);
    }
    /**
     * 获取IP解析单例
     *
     * @return Ip2regionAnalysis
     */
    public static Ip2regionAnalysis getInstance() {
        if (analysis == null) {
            synchronized (Ip2regionAnalysis.class) {
                if (analysis == null) {
                    analysis = new Ip2regionAnalysis();
                }
            }
        }
        return analysis;
    }
    /**
     * <p>方法名称:解析Ip信息.</p>
     * <p>详细描述:.</p>
     * <p>创建时间:2022-05-24 11:26:59</p>
     * <p>创建作者:lixingwu</p>
     * <p>修改记录:</p>
     *
     * @param ip IP地址
     * @return 国家|区域|省份|城市|ISP
     */
    public Optional<String> getIpInfo(String ip) {
        if (IpAddressUtil.validIp(ip)) {
            try {
                DbConfig config = new DbConfig();
                DbSearcher searcher = new DbSearcher(config, dbBinStr);
                // 搜索数据
                DataBlock search = searcher.memorySearch(ip);
                // 数据格式:国家|区域|省份|城市|ISP
                return Optional.of(search.getRegion());
            } catch (DbMakerConfigException | IOException e) {
                e.printStackTrace();
                log.error("Ip解析失败:{}", e.toString());
            }
        }
        return Optional.empty();
    }
    /**
     * <p>方法名称:解析Ip信息.</p>
     * <p>详细描述:.</p>
     * <p>创建时间:2022-05-24 11:26:59</p>
     * <p>创建作者:lixingwu</p>
     * <p>修改记录:</p>
     *
     * @param ips IP地址集合
     * @return Dict({ ip : info })
     */
    public Dict getIpInfo(HashSet<String> ips) {
        try {
            DbConfig config = new DbConfig();
            DbSearcher searcher = new DbSearcher(config, dbBinStr);
            DataBlock search;
            Dict dataset = Dict.create();
            for (String ip : ips) {
                if (IpAddressUtil.validIp(ip)) {
                    search = searcher.memorySearch(ip);
                    dataset.set(ip, search.getRegion());
                }
            }
            return dataset;
        } catch (DbMakerConfigException | IOException e) {
            e.printStackTrace();
            log.error("Ip解析失败:{}", e.toString());
        }
        return Dict.create();
    }
    /**
     * <p>方法名称:数字ip获取信息.</p>
     * <p>详细描述:.</p>
     * <p>创建时间:2022-05-24 13:15:23</p>
     * <p>创建作者:lixingwu</p>
     * <p>修改记录:</p>
     *
     * @param ip 数字IP
     * @return 国家|区域|省份|城市|ISP
     */
    public Optional<String> getIpInfo(long ip) {
        String longIpv4 = NetUtil.longToIpv4(ip);
        return getIpInfo(longIpv4);
    }
    /**
     * <p>方法名称:获取请求对象的IP信息.</p>
     * <p>详细描述:.</p>
     * <p>创建时间:2022-05-24 11:50:59</p>
     * <p>创建作者:lixingwu</p>
     * <p>修改记录:</p>
     *
     * @param request 请求对象
     * @return 国家|区域|省份|城市|ISP
     */
    public Optional<String> getIpInfo(HttpServletRequest request) {
        String ip = IpAddressUtil.getIpAddr(request);
        return getIpInfo(ip);
    }
    /**
     * <p>方法名称:获取IP信息的字典.</p>
     * <p>详细描述:.</p>
     * <p>创建时间:2022-05-24 11:52:58</p>
     * <p>创建作者:lixingwu</p>
     * <p>修改记录:</p>
     *
     * @param ip IP地址
     * @return the dict
     */
    public IpInfo getIpInfoBean(String ip) {
        Optional<String> ipInfo = getIpInfo(ip);
        IpInfo info = new IpInfo();
        if (ipInfo.isPresent()) {
            //国家|区域|省份|城市|ISP
            String[] split = StrUtil.split(ipInfo.get(), "|");
            info.setCountry(split[0]);
            info.setRegion(split[1]);
            info.setProvince(split[2]);
            info.setCity(split[3]);
            info.setIsp(split[4]);
        }
        return info;
    }
    /**
     * <p>方法名称:数字ip获取信息字典.</p>
     * <p>详细描述:.</p>
     * <p>创建时间:2022-05-24 13:15:23</p>
     * <p>创建作者:lixingwu</p>
     * <p>修改记录:</p>
     *
     * @param ip 数字IP
     * @return 国家|区域|省份|城市|ISP
     */
    public IpInfo getIpInfoBean(long ip) {
        String longIpv4 = NetUtil.longToIpv4(ip);
        return getIpInfoBean(longIpv4);
    }
    /**
     * <p>方法名称:获取IP信息的字典.</p>
     * <p>详细描述:.</p>
     * <p>创建时间:2022-05-24 11:52:58</p>
     * <p>创建作者:lixingwu</p>
     * <p>修改记录:</p>
     *
     * @param request 请求对象
     * @return the dict
     */
    public IpInfo getIpInfoBean(HttpServletRequest request) {
        String ip = IpAddressUtil.getIpAddr(request);
        return getIpInfoBean(ip);
    }
    /**
     * 测试
     */
    public static void main(String[] args) {
        log.info("121.8.215.106 \t {}", Ip2regionAnalysis.getInstance().getIpInfoBean("121.8.215.106"));
        log.info("183.247.152.98 \t {}", Ip2regionAnalysis.getInstance().getIpInfoBean("183.247.152.98"));
        log.info("14.29.139.251 \t {}", Ip2regionAnalysis.getInstance().getIpInfoBean("14.29.139.251"));
        log.info("183.247.152.98 \t {}", Ip2regionAnalysis.getInstance().getIpInfoBean("183.247.152.98"));
        log.info("27.105.130.93 \t {}", Ip2regionAnalysis.getInstance().getIpInfoBean("27.105.130.93"));
        log.info("124.205.155.147 \t {}", Ip2regionAnalysis.getInstance().getIpInfoBean("124.205.155.147"));
        // 批量解析,返回字典数据:ip:解析信息
        final HashSet<String> ipSet = CollUtil.newHashSet(
                "47.92.113.71", "221.226.75.86", "124.205.155.155",
                "47.57.188.208", "121.8.215.106", "121.8.215.106"
        );
        Dict dict = Ip2regionAnalysis.getInstance().getIpInfo(ipSet);
        log.info("{}", dict.toString());
        log.info("{}\t{}", "121.8.215.106", dict.getStr("121.8.215.106"));
    }
}

测试输出

14:19:12.791 [main] DEBUG cn.hutool.log.LogFactory - Use [Slf4j] Logger As Default.
14:19:14.150 [main] INFO util.Ip2regionAnalysis - 下载IP数据库文件...
14:19:14.633 [main] INFO util.Ip2regionAnalysis - IP数据库文件下载成功,数据库文件大小[8.33MB]
14:19:14.645 [main] INFO util.Ip2regionAnalysis - 121.8.215.106      IpInfo(country=中国, region=0, province=广东省, city=广州市, isp=电信)
14:19:14.646 [main] INFO util.Ip2regionAnalysis - 183.247.152.98      IpInfo(country=中国, region=0, province=浙江省, city=杭州市, isp=移动)
14:19:14.646 [main] INFO util.Ip2regionAnalysis - 14.29.139.251      IpInfo(country=中国, region=0, province=广东省, city=深圳市, isp=电信)
14:19:14.646 [main] INFO util.Ip2regionAnalysis - 183.247.152.98      IpInfo(country=中国, region=0, province=浙江省, city=杭州市, isp=移动)
14:19:14.646 [main] INFO util.Ip2regionAnalysis - 27.105.130.93      IpInfo(country=中国, region=0, province=台湾省, city=台北, isp=So-Net)
14:19:14.646 [main] INFO util.Ip2regionAnalysis - 124.205.155.147      IpInfo(country=中国, region=0, province=北京, city=北京市, isp=鹏博士)
14:19:14.648 [main] INFO util.Ip2regionAnalysis - {221.226.75.86=中国|0|江苏省|南京市|电信, 47.57.188.208=中国|0|香港|0|阿里云, 47.92.113.71=中国|0|河北省|张家口市|阿里云, 121.8.215.106=中国|0|广东省|广州市|电信, 124.205.155.155=中国|0|北京|北京市|鹏博士}
14:19:14.682 [main] INFO util.Ip2regionAnalysis - 121.8.215.106    中国|0|广东省|广州市|电信

在第一次调用getInstance时会去下载数据库文件会比较耗时,其他后面的操作就很快了,基本上几毫秒就查询到了。

所以如果嫌第一次慢的,可以在程序启动完成后手动调用,预热一下,在实际业务就会使用缓存的数据库了。

实际使用

在项目中我编写了一个 CommonController.java ,然后使用编写的类提供了一个接口,用于获取IP的信息。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import cn.hutool.extra.servlet.ServletUtil;
import com.yunding.vote.common.api.CommonResult;
import com.yunding.vote.common.limiter.RInterfaceLimit;
import com.yunding.vote.domain.IpInfo;
import com.yunding.vote.util.Ip2regionAnalysis;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
 * 公共控制层,该类方法不会记录日志
 */
@RestController
@Api(tags = "公共控制层", description = "公共控制层")
@RequestMapping("/common")
@Slf4j
public class CommonController {
    @Resource
    private HttpServletRequest request;
    @ApiOperation("获取IP的信息,100QPS")
    @GetMapping(value = "/ipInfo/{ip}")
    @RInterfaceLimit(rate = 100)
    public CommonResult<IpInfo> getIpInfo(@PathVariable String ip) {
        String clientIp = ServletUtil.getClientIP(request);
        IpInfo ipInfo = Ip2regionAnalysis.getInstance().getIpInfoBean(ip);
        return CommonResult.success(ipInfo);
    }
}

上面这个类只是告诉大家是怎么使用Ip2regionAnalysis这个类的,大家根据自己的项目自行调整。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET http://127.0.0.1:8080/api/v1/common/ipInfo/121.8.215.106
 
>>>
{
  "code": 200,
  "data": {
    "country": "中国",
    "region": 0,
    "province": "广东省",
    "city": "广州市",
    "isp": "电信"
  },
  "message": "操作成功"
}

到此这篇关于JAVA使用Ip2region获取IP定位信息的文章就介绍到这了,更多相关java获取IP定位信息内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/lixingwu/p/16317568.html

延伸 · 阅读

精彩推荐
  • Java教程Java基础教程之继承详解

    Java基础教程之继承详解

    这篇文章主要介绍了Java基础教程之继承详解,继承是除组合(composition)之外,提高代码重复可用性(reusibility)的另一种重要方式,本文对继承做了详细讲解,需要的...

    java教程网2622019-11-27
  • Java教程Mybatis模糊查询和动态sql语句的用法

    Mybatis模糊查询和动态sql语句的用法

    今天小编就为大家分享一篇关于Mybatis模糊查询和动态sql语句的用法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随...

    LLY199604184622021-07-26
  • Java教程Java动态规划之编辑距离问题示例代码

    Java动态规划之编辑距离问题示例代码

    这篇文章主要介绍了Java动态规划之编辑距离问题示例代码,具有一定参考价值,需要的朋友可以了解下。...

    SilentKnight11982021-02-23
  • Java教程关于JDBC的简单封装(实例讲解)

    关于JDBC的简单封装(实例讲解)

    下面小编就为大家带来一篇关于JDBC的简单封装(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    Java教程网3832020-12-12
  • Java教程springboot动态调整日志级别的操作大全

    springboot动态调整日志级别的操作大全

    这篇文章主要介绍了springboot动态调整日志级别的方法,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需...

    forwardMyLife4632022-02-20
  • Java教程Java微信公众平台开发(12) 微信用户信息的获取

    Java微信公众平台开发(12) 微信用户信息的获取

    这篇文章主要为大家详细介绍了Java微信公众平台开发第十二步,微信用户信息的获取,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    dapengniao5282020-09-18
  • Java教程MyBatis-Plus实现多数据源的示例代码

    MyBatis-Plus实现多数据源的示例代码

    这篇文章主要介绍了MyBatis-Plus实现多数据源的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    youcongtech4682021-09-24
  • Java教程Java内存划分:运行时数据区域

    Java内存划分:运行时数据区域

    听说Java运行时环境的内存划分是挺进BAT的必经之路,这篇文章主要给大家介绍了关于Java运行时数据区域(内存划分)的相关资料,需要的朋友可以参考下...

    一颗苹果.10452021-10-06