IP获取归属地,通过地址库获取
如果使用API接口获取,可能会出现服务挂了,或者服务地址不提供服务了等问题。而采用本地地址库就没有这些问题。
本文采用离线IP地址定位库 Ip2region,Ip2region是一个离线IP地址定位库,微秒的查询时间。
实现步骤
- 访问官网github地址:https://github.com/lionsoul2014/ip2region
- 找到data目录下的:ip2region.xdb文件下载下来
- 把ip2region.xdb文件放在resources目录下
- 在模块中引入maven依赖
<!--解析ip地址-->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
- 获取归属地工具类
package com.sinosoft.springbootplus.util;
import cn.hutool.core.io.resource.ResourceUtil;
import com.sinosoft.springbootplus.common.api.ApiCode;
import com.sinosoft.springbootplus.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.flywaydb.core.internal.util.FileCopyUtils;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* ip解析工具
*
*/
@Slf4j
public class IpParseUtil {
/**
* 将整个xdb文件加载到内存中(11M左右),此种创建方式支持多线程,因此只需要加载一次
*/
private static Searcher SEARCHER;
static {
try (InputStream inputStream = new ClassPathResource("ip2region.xdb").getInputStream()){
byte[] cBuff = IOUtils.toByteArray(inputStream);
SEARCHER = Searcher.newWithBuffer(cBuff);
log.info("加载了ip2region.xdb文件,Searcher初始化完成!");
} catch (Exception e) {
log.error("初始化ip2region.xdb文件失败,报错信息:[{}]", e.getMessage(), e);
throw new BusinessException(ApiCode.SYSTEM_EXCEPTION);
}
}
/**
* 解析ip地址
*
* @param ipStr 字符串类型ip 例:192.168.0.1
* @return 返回结果形式(国家|区域|省份|城市|ISP) 例 [中国, 0, 河北省, 衡水市, 电信]
*/
public static List<String> parse(@NotBlank String ipStr) {
return parse(ipStr, null);
}
/**
* 自定义解析ip地址
*
* @param ipStr ip 字符串类型ip 例:1970753539(经过转换后的)
* @param index 想要获取的区间 例如:只想获取 省,市 index = [2,3]
* @return 返回结果例 [北京,北京市]
*/
public static List<String> parse(@NotBlank String ipStr, int[] index) {
try {
long ip = Searcher.checkIP(ipStr);
return parse(ip, index);
} catch (Exception e) {
log.error("ip解析为long错误,ipStr:[{}],错误信息:[{}]", ipStr, e.getMessage(), e);
throw new BusinessException(ApiCode.SYSTEM_EXCEPTION);
}
}
/**
* 自定义解析ip地址
*
* @param ip ip Long类型ip
* @param index 想要获取的区间 例如:只想获取 省,市 index = [2,3]
* @return 返回结果例 [河北省, 衡水市]
*/
public static List<String> parse(@NotNull Long ip, int[] index) {
//获取xdb文件资源
List<String> regionList = new ArrayList<>();
try {
String region = SEARCHER.search(ip);
String[] split = region.split("\\|");
if (index == null) {
regionList = Arrays.asList(split);
} else {
for (int i : index) {
if ("0".equals(split[i])) {
split[i] = "——";
}
regionList.add(split[i]);
}
}
//关闭资源
SEARCHER.close();
} catch (Exception e) {
log.error("根据ip解析地址失败,ip:[{}],index[{}],报错信息:[{}]", ip, index, e.getMessage(), e);
throw new BusinessException(ApiCode.SYSTEM_EXCEPTION);
}
return regionList;
}
}
这里Searcher引用的是仓库里面的检索方法,到这里就完成了可以获取到ip对应的归属地。
- 测试类
package com.sinosoft.springbootplus;
import com.sinosoft.springbootplus.util.IpParseUtil;
import java.util.List;
/**
* @author lsh
* @date 2024/3/21
*/
public class IPTest {
public static void main(String[] args) {
//只获取省,市
int[] index = {2, 3};
List<String> parse = IpParseUtil.parse("222.223.145.133", index);
System.out.println(parse);
}
}
结果
遇到的问题
包后读取到的文件流大小发生变化,无法根据ip识别到对应的地址信息。
1、正常读取到的文件大小如下图
打成jar包后,在依赖项目中运行,读取到的文件流如下图
2、出现的原因是打包时文件被压缩了
3、解决办法,在pom文件中增加下面的配置,打包时不压缩后缀为xdb格式的文件
<plugins>
<!--
避免在resource是目下,后缀为 xdb文件被压缩,导致不可以读,此处做排除操作。
如果有新的类型的文件可以再后边追加
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>xdb</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>