import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbMakerConfigException;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.apobates.forum.utils.lang.Result;
/**
* IP地址匹配器,使用开源项目(ip2region)
* {@link https://gitee.com/lionsoul/ip2region}
*
* @author xiaofanku
* @since 20191206
*/
public final class IPMatcher {
/**
* 失败时的返回的占位符
*/
public final static String MARK="-";
private static IPMatcher instance=null;
private final DbSearcher searcher;
private final static Logger logger = LoggerFactory.getLogger(IPMatcher.class);
static {
try{
instance = new IPMatcher();
}catch(Exception e){
if(logger.isDebugEnabled()){
logger.debug("[IPM]instace exception: "+e.getMessage());
}
}
}
public static IPMatcher getInstance() {
return instance;
}
private IPMatcher() throws DbMakerConfigException, IOException{
// db
InputStream istream = this.getClass().getClassLoader().getResourceAsStream("META-INF/ip2region.db");
File targetFile = File.createTempFile("ip2region", ".db");
java.nio.file.Files.copy(istream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
if (targetFile.exists() == false) {
throw new IllegalStateException("[IPM]database file Lost");
}
DbConfig config = new DbConfig();
this.searcher = new DbSearcher(config, targetFile.getPath());
}
/**
* 查找方法
*
* @param ip ip地址
* @return
* @throws IllegalStateException 对查找过程产生异常的包装
* @throws IllegalArgumentException ip地址检查失败时抛出
* @throws NullPointerException 若参数是null时抛出
*/
private String getCityInfo(String ip)throws IllegalStateException, IllegalArgumentException, NullPointerException {
Objects.requireNonNull(ip);
try {
if (Util.isIpAddress(ip) == false) {
throw new IllegalArgumentException("参数不符合合法的地址");
}
DataBlock dataBlock = searcher.memorySearch(ip);//.btreeSearch(ip);
return dataBlock.getRegion();
} catch (NullPointerException | IOException e) {
if(logger.isDebugEnabled()){
logger.debug("[IPM]get city info exception: "+e.getMessage());
}
throw new IllegalStateException(e);
}
}
/**
* 返回字符串的匹配结果
*
* @param ipAddr ip地址
* @return
*/
public Result<String> matchToString(String ipAddr){
if(isLoopbackIp(ipAddr)){
return Result.empty();
}
try{
String data = getCityInfo(ipAddr);
return Result.ofNullable(data);
}catch(IllegalStateException | IllegalArgumentException | NullPointerException e){
if(logger.isDebugEnabled()){
logger.debug("[IPM]matchToString exception: "+e.getMessage());
}
return Result.failure(e.getMessage());
}
}
/**
* 返回IpMatchResult的匹配结果
*
* @param ipAddr ip地址
* @return
*/
public Result<IpMatchResult> matchToResult(String ipAddr){
if(isLoopbackIp(ipAddr)){
return Result.empty();
}
try{
String[] data=getCityInfo(ipAddr).split("\\|"); //国家|大区|省份|城市|运营商
return Result.success(new IpMatchResult(ipAddr, data[4], data[2], data[3], MARK));
}catch(IllegalStateException | IllegalArgumentException | NullPointerException | ArrayIndexOutOfBoundsException e){
if(logger.isDebugEnabled()){
logger.debug("[IPM]matchToResult exception: "+e.getMessage());
}
return Result.failure(e.getMessage());
}
}
/**
* 是否是本地的环回地址
*
* @param ipAddr ip地址
* @return true是,false不是
*/
public static boolean isLoopbackIp(String ipAddr){
try{
return Inet4Address.getByName(ipAddr).isLoopbackAddress();
}catch(UnknownHostException e){
return false;
}
}
public static class IpMatchResult{
private final String ipAddr;
//供应商:联通|电信
private final String isp;
//地域:省
private final String province;
//地域:市
private final String city;
//地域:区
private final String district;
public IpMatchResult(String ipAddr, String isp, String province, String city, String district) {
this.ipAddr = ipAddr;
this.isp = isp;
this.province = province;
this.city = city;
this.district = district;
}
/**
* 返回ISP供应商,例:联通
* @return
*/
public String getIsp() {
return isp;
}
/**
* 返回省份,例:山东省
* @return
*/
public String getProvince() {
return province;
}
/**
* 返回市,例:烟台市
* @return
*/
public String getCity() {
return city;
}
/**
* 返回区或县
* @return
*/
public String getDistrict() {
return district;
}
public String getIpAddr() {
return ipAddr;
}
@Override
public String toString() {
return String.format("{\"IP\":\"%s\", \"ISP供应商\":\"%s\", \"省\":\"%s\", \"市\": \"%s\"}", getIpAddr(), getIsp(), getProvince(), getCity());
}
}
}
主类修正后的源码@20191213
import java.util.Arrays;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import com.apobates.forum.utils.ip.IPMatcher;
import com.apobates.forum.utils.ip.IPMatcher.IpMatchResult;
public class IPMatcherTest {
@Test
public void testIPM(){
String ip = "27.216.186.85";//27.216.186.85
IpMatchResult mr = IPMatcher.getInstance().matchToResult(ip).getOrElse(()->null);
if(mr != null){
//省: 山东省, 市: 烟台市, ISP: 联通
System.out.println("省: "+mr.getProvince()+", 市: "+mr.getCity()+", ISP: "+mr.getIsp());
}
}
@Test
public void testIPMS(){
String ip = "27.216.186.85";//27.216.186.85
String str = IPMatcher.getInstance().matchToString(ip).getOrElse("*");
System.out.println("结果: "+str); //中国|0|山东省|烟台市|联通
}
@Test
public void batchIPM(){
List<String> ips = Arrays.asList("127.0.0.111", "127.0.0.1", "192.168.0.18", "192.168.0.32", "192.168.0.24", "192.168.0.76",
"192.168.0.23", "192.168.0.69", "0:0:0:0:0:0:0:1", "122.5.29.130","27.217.117.215", "123.130.141.13", "27.217.119.35",
"223.104.4.120", "106.39.189.28", "117.136.46.70", "218.90.102.67", "117.136.45.127", "39.73.162.10", "121.235.97.60",
"123.130.50.82", "123.130.163.74", "39.73.124.229", "39.73.125.148", "58.247.212.140", "223.104.13.156", "223.104.3.194",
"27.216.162.51", "14.215.176.6", "14.215.176.12", "123.232.37.131", "39.86.234.19", "123.233.124.229", "100.116.251.34",
"100.116.251.112", "100.116.251.72", "100.116.251.52", "100.116.251.37", "100.116.251.43", "122.5.29.130", "100.116.251.85",
"100.116.251.57", "122.5.29.130", "100.116.251.58", "100.116.236.232", "100.116.236.224", "100.116.236.230", "100.116.236.234",
"100.116.251.238", "100.116.251.230", "100.116.251.208", "100.116.251.218", "100.116.236.231", "100.116.236.235",
"100.116.236.238", "100.116.251.219", "100.116.251.235", "100.116.251.234", "100.116.251.222", "100.116.251.233");
final IPMatcher ipm = IPMatcher.getInstance();
ips.stream().parallel().map(ip->ipm.matchToResult(ip)).forEach(System.out::println);
}
}
这个很明现因为要载入ip数据库. 本人比较菜,可能这个单例写法不一定完美. 不要像有些文档说的写一个静态工具方法。如果调用频繁每次都载入ip数据库是很浪费时间的。另外在实际项目中必需使用内存查找.
是一个类Optional的工具类,提供三种情况: 空,异常,正常三种情况。代码在哪呢?请看聊聊WEB项目中的图片的G1中找到下载地址
这个文件我也是在官方下载的.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<ip2region.version>1.7.2</ip2region.version>
<slf4j.version>1.7.25</slf4j.version>
</properties>
若有更完美的写法也请您与小可分享一下,在此先谢过了。