广丰卷烟厂数采质量分析系统
zhuguifei
2026-03-02 80ff784bf60637cd348ae665fc907f7b1e527dd8
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
package org.dromara.common.core.utils.ip;
 
import cn.hutool.core.io.resource.ResourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.lionsoul.ip2region.service.Config;
import org.lionsoul.ip2region.service.Ip2Region;
import org.lionsoul.ip2region.xdb.Util;
 
import java.io.InputStream;
import java.time.Duration;
 
/**
 * IP地址行政区域工具类
 * 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">ip2region xdb java 查询客户端实现</a>
 * xdb数据库文件下载:<a href="https://gitee.com/lionsoul/ip2region/tree/master/data">ip2region data</a>
 *
 * @author 秋辞未寒
 */
@Slf4j
public class RegionUtils {
 
    // 默认IPv4地址库文件路径
    // 下载地址:https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v4.xdb
    public static final String DEFAULT_IPV4_XDB_PATH = "ip2region_v4.xdb";
 
    // 默认IPv6地址库文件路径
    // 下载地址:https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v6.xdb
    public static final String DEFAULT_IPV6_XDB_PATH = "ip2region_v6.xdb";
 
    // 默认缓存切片大小为15MB(仅针对BufferCache全量读取有效,如果你的xdb数据库很大,合理设置该值可以有效提升BufferCache模式下的查询效率,具体可以查看Ip2Region的README)
    // 注意:设置过大的值可能会申请内存时,因内存不足而导致OOM,请合理设置该值。
    // README:https://gitee.com/lionsoul/ip2region/tree/master/binding/java
    public static final int DEFAULT_CACHE_SLICE_BYTES = 1024 * 1024 * 15;
 
    // 未知地址
    public static final String UNKNOWN_ADDRESS = "未知";
 
    // Ip2Region服务实例
    private static Ip2Region ip2Region;
 
    // 初始化Ip2Region服务实例
    static {
        try {
            // 注意:Ip2Region 的xdb文件加载策略 CachePolicy 有三种,分别是:BufferCache(全量读取xdb到内存中)、VIndexCache(默认策略,按需读取并缓存)、NoCache(实时读取)
            // 本项目工具使用的 CachePolicy 为 BufferCache,BufferCache会加载整个xdb文件到内存中,setXdbInputStream 仅支持 BufferCache 策略。
            // 因为加载整个xdb文件会耗费非常大的内存,如果你不希望加载整个xdb到内存中,更推荐使用 VIndexCache 或 NoCache(即实时读取文件)策略和 setXdbPath/setXdbFile 加载方法(需要注意的一点,setXdbPath 和 setXdbFile 不支持读取ClassPath(即源码和resource目录)中的文件)。
            // 一般而言,更建议把xdb数据库放到一个指定的文件目录中(即不打包进jar包中),然后使用 VIndexCache + 配合SearcherPool的并发池读取数据,更方便随时更新xdb数据库
 
            InputStream v4InputStream = ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH);
 
            // IPv4配置
            Config v4Config = Config.custom()
                .setCachePolicy(Config.BufferCache)
                //.setXdbFile(v4TempXdb)
                .setXdbInputStream(v4InputStream)
                //
                .setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
                .asV4();
 
            // IPv6配置
            Config v6Config = null;
            InputStream v6XdbInputStream = ResourceUtil.getStreamSafe(DEFAULT_IPV6_XDB_PATH);
            if (v6XdbInputStream == null) {
                log.warn("未加载 IPv6 地址库:未在类路径下找到文件 {}。当前仅启用 IPv4 查询。如需启用 IPv6,请将 ip2region_v6.xdb 放置到 resources 目录", DEFAULT_IPV6_XDB_PATH);
            } else {
                v6Config = Config.custom()
                    .setCachePolicy(Config.BufferCache)
                    //.setXdbFile(v6TempXdb)
                    .setXdbInputStream(v6XdbInputStream)
                    .setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
                    .asV6();
            }
 
            // 初始化Ip2Region实例
            RegionUtils.ip2Region = Ip2Region.create(v4Config, v6Config);
            log.debug("IP工具初始化成功,加载IP地址库数据成功!");
        } catch (Exception e) {
            throw new ServiceException("RegionUtils初始化失败,原因:{}", e.getMessage());
        }
    }
 
    /**
     * 根据IP地址离线获取城市
     *
     * @param ipString ip地址字符串
     */
    public static String getRegion(String ipString) {
        try {
            String region = ip2Region.search(ipString);
            if (StringUtils.isBlank(region)) {
                region = UNKNOWN_ADDRESS;
            }
            return region;
        } catch (Exception e) {
            log.error("IP地址离线获取城市异常 {}", ipString);
            return UNKNOWN_ADDRESS;
        }
 
    }
 
    /**
     * 根据IP地址离线获取城市
     *
     * @param ipBytes ip地址字节数组
     */
    public static String getRegion(byte[] ipBytes) {
        try {
            String region = ip2Region.search(ipBytes);
            if (StringUtils.isBlank(region)) {
                region = UNKNOWN_ADDRESS;
            }
            return region;
        } catch (Exception e) {
            log.error("IP地址离线获取城市异常 {}", Util.ipToString(ipBytes));
            return UNKNOWN_ADDRESS;
        }
    }
 
    /**
     * 关闭Ip2Region服务
     */
    public static void close() {
        if (ip2Region == null) {
            return;
        }
        try {
            ip2Region.close(10000);
        } catch (Exception e) {
            log.error("Ip2Region服务关闭异常", e);
        }
    }
 
    /**
     * 关闭Ip2Region服务
     *
     * @param timeout 关闭超时时间
     */
    public static void close(final Duration timeout) {
        if (ip2Region == null) {
            return;
        }
        if (timeout == null) {
            close();
            return;
        }
        try {
            ip2Region.close(timeout.toMillis());
        } catch (Exception e) {
            log.error("Ip2Region服务关闭异常", e);
        }
    }
 
}