自动计算从A向量变换到B向量的旋转矩阵和缩放因子
1. 基础数学原理对于两个向量 v₁ 和 v₂，我们要找到旋转矩阵 R 和缩放因子 s，使得：v₂ = s × R × v₁2. Java实现public class VectorTransform { private double[][] rotationMatrix; private double scaleFactor; public VectorTransform(double[] sourceVector, double[] targetVector) { if (sourceVector.length != targetVector.length) { throw new IllegalArgumentException("Vectors must have same dimension"); } calculateTransform(sourceVector, targetVector); } private void calculateTransform(double[] v1, double[] v2) { int n = v1.length; // 计算缩放因子 (向量模长的比值) double normV1 = norm(v1); double normV2 = norm(v2); if (normV1 == 0) { throw new IllegalArgumentException("Source vector cannot be zero vector"); } this.scaleFactor = normV2 / normV1; // 计算旋转矩阵 if (n == 2) { calculateRotation2D(v1, v2); } else if (n == 3) { calculateRotation3D(v1, v2); } else { calculateRotationND(v1, v2); } } // 计算二维旋转 private void calculateRotation2D(double[] v1, double[] v2) { double[] u1 = normalize(v1); double[] u2 = normalize(v2); double cosTheta = dot(u1, u2); double sinTheta = u1[0] * u2[1] - u1[1] * u2[0]; // 2D叉积 rotationMatrix = new double[][]{ {cosTheta, -sinTheta}, {sinTheta, cosTheta} }; } // 计算三维旋转 (使用罗德里格斯旋转公式) private void calculateRotation3D(double[] v1, double[] v2) { double[] u1 = normalize(v1); double[] u2 = normalize(v2); double[] axis = cross(u1, u2); double axisNorm = norm(axis); if (axisNorm < 1e-10) { // 向量平行或反平行 if (dot(u1, u2) > 0) { // 平行 - 单位矩阵 rotationMatrix = new double[][]{ {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }; } else { // 反平行 - 180度旋转 (任意轴) rotationMatrix = new double[][]{ {-1, 0, 0}, {0, -1, 0}, {0, 0, -1} }; } } else { double[] unitAxis = normalize(axis); double cosTheta = dot(u1, u2); double sinTheta = axisNorm; // 罗德里格斯旋转公式 double x = unitAxis[0], y = unitAxis[1], z = unitAxis[2]; double c = cosTheta; double s = sinTheta; double t = 1 - c; rotationMatrix = new double[][]{ {t*x*x + c, t*x*y - s*z, t*x*z + s*y}, {t*x*y + s*z, t*y*y + c, t*y*z - s*x}, {t*x*z - s*y, t*y*z + s*x, t*z*z + c} }; } } // 计算N维旋转 (使用Householder变换) private void calculateRotationND(double[] v1, double[] v2) { int n = v1.length; double[] u = normalize(v1); double[] v = normalize(v2); double[] w = subtract(u, v); double wNorm = norm(w); if (wNorm < 1e-10) { // 向量相同 - 单位矩阵 rotationMatrix = identityMatrix(n); return; } w = normalize(w); // Householder 反射矩阵: H = I - 2wwᵀ rotationMatrix = identityMatrix(n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { rotationMatrix[i][j] -= 2 * w[i] * w[j]; } } } // 应用变换到新向量 public double[] transform(double[] vector) { if (vector.length != rotationMatrix.length) { throw new IllegalArgumentException("Vector dimension mismatch"); } double[] rotated = multiply(rotationMatrix, vector); return multiply(rotated, scaleFactor); } // 获取变换参数 public double[][] getRotationMatrix() { return copyMatrix(rotationMatrix); } public double getScaleFactor() { return scaleFactor; } // 工具方法 public double norm(double[] v) { return Math.sqrt(dot(v, v)); } public double[] normalize(double[] v) { double n = norm(v); if (n == 0) return v; return multiply(v, 1.0 / n); } private double dot(double[] a, double[] b) { double result = 0; for (int i = 0; i < a.length; i++) { result += a[i] * b[i]; } return result; } private double[] cross(double[] a, double[] b) { if (a.length != 3 || b.length != 3) { throw new IllegalArgumentException("Cross product only for 3D vectors"); } return new double[]{ a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0] }; } private double[] subtract(double[] a, double[] b) { double[] result = new double[a.length]; for (int i = 0; i < a.length; i++) { result[i] = a[i] - b[i]; } return result; } private double[] multiply(double[] v, double scalar) { double[] result = new double[v.length]; for (int i = 0; i < v.length; i++) { result[i] = v[i] * scalar; } return result; } private double[] multiply(double[][] matrix, double[] vector) { int n = matrix.length; double[] result = new double[n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { result[i] += matrix[i][j] * vector[j]; } } return result; } private double[][] identityMatrix(int n) { double[][] I = new double[n][n]; for (int i = 0; i < n; i++) { I[i][i] = 1; } return I; } private double[][] copyMatrix(double[][] matrix) { double[][] copy = new double[matrix.length][]; for (int i = 0; i < matrix.length; i++) { copy[i] = matrix[i].clone(); } return copy; } }3. 使用示例public class VectorTransformExample { public static void main(String[] args) { // 示例1: 2D向量变换 double[] source2D = {1, 0}; // 原始向量 double[] target2D = {0, 2}; // 目标向量 VectorTransform transform2D = new VectorTransform(source2D, target2D); // 对新向量应用相同的变换 double[] newVector2D = {2, 1}; double[] transformed2D = transform2D.transform(newVector2D); System.out.println("2D Transform:"); System.out.println("Scale factor: " + transform2D.getScaleFactor()); System.out.println("Original: " + Arrays.toString(newVector2D)); System.out.println("Transformed: " + Arrays.toString(transformed2D)); // 示例2: 3D向量变换 double[] source3D = {1, 0, 0}; double[] target3D = {0, 1, 0}; VectorTransform transform3D = new VectorTransform(source3D, target3D); double[] newVector3D = {1, 1, 1}; double[] transformed3D = transform3D.transform(newVector3D); System.out.println("\n3D Transform:"); System.out.println("Scale factor: " + transform3D.getScaleFactor()); System.out.println("Original: " + Arrays.toString(newVector3D)); System.out.println("Transformed: " + Arrays.toString(transformed3D)); } }4. 高级版本（支持变换组合）public class AdvancedVectorTransform { private double[][] transformationMatrix; public AdvancedVectorTransform(double[] sourceVector, double[] targetVector) { // 直接计算完整的仿射变换矩阵 this.transformationMatrix = calculateTransformationMatrix(sourceVector, targetVector); } private double[][] calculateTransformationMatrix(double[] v1, double[] v2) { int n = v1.length; double[][] matrix = new double[n][n]; // 这里可以使用更复杂的算法，如SVD分解 // 简化为缩放和旋转的组合 VectorTransform basicTransform = new VectorTransform(v1, v2); double scale = basicTransform.getScaleFactor(); double[][] rotation = basicTransform.getRotationMatrix(); // 组合缩放和旋转 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { matrix[i][j] = scale * rotation[i][j]; } } return matrix; } public double[] transform(double[] vector) { return multiply(transformationMatrix, vector); } // 矩阵乘法工具方法 private double[] multiply(double[][] matrix, double[] vector) { int n = matrix.length; double[] result = new double[n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { result[i] += matrix[i][j] * vector[j]; } } return result; } }
https://blog.csdn.net/Lwehne/article/details/135867512如果提示需要登录Oracle账号，这里提供一个：# 提醒：为了大伙的方便，请不要随便用该邮箱重新注册Oracle账号！ 账号：yawoniu@163.com 密码：Oracle.123
Jsoup概述org.jsoup.Jsoup 是一款强大的Java HTML解析库，它能够：🚀 从URL/文件/字符串加载HTML文档🔍 使用DOM遍历提取数据（类似jQuery语法）✂️ 清理用户输入防止XSS攻击🛠️ 操作HTML元素实现动态修改📦 零依赖轻量级设计（仅280KB）添加依赖（Maven）<dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.16.2</version> </dependency>创建清理工具类import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; public class XssCleaner { // 基本HTML标签白名单 private static final Safelist BASIC_WHITELIST = Safelist.basic() .addTags("div", "span", "img") .addAttributes("img", "src", "alt", "title") .addProtocols("img", "src", "http", "https"); public static String clean(String dirty) { return dirty == null ? null : Jsoup.clean(dirty, BASIC_WHITELIST); } }创建可重复读取的请求包装器import org.springframework.util.StreamUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { private final byte[] cachedBody; public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { super(request); InputStream inputStream = request.getInputStream(); this.cachedBody = StreamUtils.copyToByteArray(inputStream); } @Override public ServletInputStream getInputStream() { return new CachedBodyServletInputStream(cachedBody); } @Override public BufferedReader getReader() { return new BufferedReader(new InputStreamReader(this.getInputStream())); } public static class CachedBodyServletInputStream extends ServletInputStream { private final ByteArrayInputStream buffer; public CachedBodyServletInputStream(byte[] contents) { this.buffer = new ByteArrayInputStream(contents); } @Override public synchronized void reset() { buffer.reset(); } @Override public int read() { return buffer.read(); } @Override public boolean isFinished() { return buffer.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { throw new UnsupportedOperationException(); } } }创建自定义参数过滤器import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.thsware.framework.util.XssCleaner; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.MediaType; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Arrays; @Order(Ordered.HIGHEST_PRECEDENCE) public class XssFilter extends OncePerRequestFilter { private static final ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { // 处理json请求 if(StringUtils.startsWithIgnoreCase(req.getContentType(), MediaType.APPLICATION_JSON_VALUE)){ CachedBodyHttpServletRequest requestWrapper = new CachedBodyHttpServletRequest(req); // 获取原始JSON JsonNode rootNode = objectMapper.readTree(requestWrapper.getInputStream()); // 递归清理所有字符串值 cleanJsonNode(rootNode); // 将清理后的JSON写回请求 byte[] cleanedJson = objectMapper.writeValueAsBytes(rootNode); requestWrapper = new CachedBodyHttpServletRequest(req) { @Override public ServletInputStream getInputStream() { return new CachedBodyServletInputStream(cleanedJson); } }; chain.doFilter(requestWrapper, res); return; } // 处理非json请求 chain.doFilter(new XssRequestWrapper(req), res); } private void cleanJsonNode(JsonNode node) { if (node.isObject()) { ObjectNode objectNode = (ObjectNode) node; objectNode.fields().forEachRemaining(entry -> { if (entry.getValue().isTextual()) { // 清理文本值 String cleaned = XssCleaner.clean(entry.getValue().asText()); objectNode.put(entry.getKey(), cleaned); } else if (entry.getValue().isObject() || entry.getValue().isArray()) { // 递归处理嵌套对象/数组 cleanJsonNode(entry.getValue()); } }); } else if (node.isArray()) { node.elements().forEachRemaining(this::cleanJsonNode); } } } class XssRequestWrapper extends HttpServletRequestWrapper { public XssRequestWrapper(HttpServletRequest request) { super(request); } @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if (values == null) return null; return Arrays.stream(values) .map(XssCleaner::clean) .toArray(String[]::new); } }注册拦截器@Configuration public class FilterConfig { @Bean public FilterRegistrationBean<JsonXssFilter> jsonXssFilter() { FilterRegistrationBean<JsonXssFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new JsonXssFilter()); registration.addUrlPatterns("/*"); registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 设置高优先级 return registration; } }
必须关闭自动提交，否则频繁提交事务会影响速度// 使用预处理语句+批处理 String baseSQL = "INSERT INTO my_table (col1, col2) VALUES (?, ?)"; try (Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db"); PreparedStatement pstmt = conn.prepareStatement(baseSQL)) { conn.setAutoCommit(false); // 关闭自动提交 // 添加批处理参数 for (int i = 0; i < 10000; i++) { pstmt.setInt(1, i); pstmt.setString(2, "value_" + i); pstmt.addBatch(); if (i % 1000 == 0) { // 每1000条执行一次 pstmt.executeBatch(); } } pstmt.executeBatch(); // 执行剩余批处理 conn.commit(); // 提交事务 } catch (SQLException e) { e.printStackTrace(); }
Java通过定时执行线程保持连接，防止超时
boolean result = false;ScheduledExecutorService scheduler = null;try {// 创建定时任务 scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(() -> { // 执行查询保持sql连接活跃 documentObjectService.findOne(modelObjectId).orElse(null); }, 0, 10, TimeUnit.MINUTES); // 每10分钟执行一次 result = createPropDb(new File(this.uploadBasePath + loadPath));} finally {// 释放定时任务 if (scheduler != null) { scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); } }}
通过aspose将word使用表格模板动态创建表格并导出
// 模板路径 ClassPathResource classPathResource = new ClassPathResource("/templates/DailyTemplate.docx"); try (InputStream inputStream = classPathResource.getInputStream()) { Document document = new Document(inputStream); // 表格模板路径 ClassPathResource tablePathResource = new ClassPathResource("/templates/TableDailyTemplate.docx"); try (InputStream tableInputStream = tablePathResource.getInputStream()) { Document table = new Document(tableInputStream); // 获取源文档中的表格 Table tableToCopy = (Table) table.getChild(NodeType.TABLE, 0, true); // 因为表格从第四行开始是需要动态增长的，所以取第四行作为模板 Row baseRow = tableToCopy.getRows().get(4); // 插入数据 for (int i = 0; i < dtoList.size(); i++) { DataDTO dataDTO = dtoList.get(i); Row newRow; if (i == 0) { // 第一行数据直接录入模板行 newRow = baseRow; } else { // 深度克隆模板行，含样式 newRow = (Row) baseRow.deepClone(true); } // 设置第一列值，后续所有列都可参照该方式设置 Cell newCell = newRow.getCells().get(0); newCell.removeAllChildren(); newCell.appendChild(new Paragraph(table)); newCell.getFirstParagraph().appendChild(new Run(table, dataDTO.getCode())); // 内容居中 newCell.getFirstParagraph().getParagraphFormat().setAlignment(ParagraphAlignment.CENTER); if (i > 0) { // 非模板行插入行 tableToCopy.getRows().insert(4 + i, newRow); } } // 克隆表格到目标文档 Node importedNode = document.importNode(tableToCopy, true); document.getLastSection().getBody().appendChild(importedNode); // 以下内容为需要动态往word文档中插入多个表格模板时分隔表格的方式 // 创建分页符 Paragraph blankParagraph = new Paragraph(document); Run run = new Run(document); run.setText(ControlChar.PAGE_BREAK); // 插入分页控制字符 blankParagraph.appendChild(run); // 插入分页符 document.getLastSection().getBody().appendChild(blankParagraph); // // 创建空白段落 // Paragraph blankParagraph = new Paragraph(document); // blankParagraph.appendChild(new Run(document, "\n")); // blankParagraph.getParagraphFormat().setSpaceBefore(10); // 设置段前间距 // blankParagraph.getParagraphFormat().setSpaceAfter(10); // 设置段后间距 // // 插入空白行 // document.getLastSection().getBody().appendChild(blankParagraph); } // 检测最后的内容是否是分页符，如果是则移除，避免空白页 Body body = document.getLastSection().getBody(); Node lastNode = body.getLastChild(); if (lastNode instanceof Paragraph) { Paragraph lastPara = (Paragraph) lastNode; Node lastRunNode = lastPara.getLastChild(); if (lastRunNode instanceof Run) { Run lastRun = (Run) lastRunNode; if (lastRun.getText().equals(ControlChar.PAGE_BREAK)) { // 情况 1：段落仅包含分页符 Run，直接删除整个段落 if (lastPara.getChildNodes().getCount() == 1) { body.removeChild(lastNode); } // 情况 2：段落包含其他内容，仅删除分页符 Run else { lastPara.removeChild(lastRun); } } } } // 保存文件为word文档 document.save("D:\demo.docx", SaveFormat.DOCX); // 保存文件为pdf document.save("D:\demo.pdf", SaveFormat.PDF); }
引入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.42</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.6.0</version> </dependency>代码import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; /** * deepseek工具类 */ @Component public class DeepSeekUtil { private final Logger log = LoggerFactory.getLogger(DeepSeekUtil.class); private static OkHttpClient client; private final static String CHAT_URL = "/v1/chat/completions"; private static String deepseekHost; @Value("${deepseekHost}") public void setDeepseekHost(String deepseekHost) { DeepSeekUtil.deepseekHost = deepseekHost; } private static String deepseekChatUrl; @Value("${deepseekChatUrl}") public void setDeepseekChatUrl(String deepseekChatUrl) { DeepSeekUtil.deepseekChatUrl = deepseekChatUrl; } private static String deepseekApiKey; @Value("${deepseekApiKey}") public void setDeepseekApiKey(String deepseekApiKey) { DeepSeekUtil.deepseekApiKey = deepseekApiKey; } private static String deepseekModel; @Value("${deepseekModel}") public void setDeepseekModel(String deepseekModel) { DeepSeekUtil.deepseekModel = deepseekModel; } DeepSeekUtil(){ client = new OkHttpClient.Builder() .connectTimeout(60, TimeUnit.SECONDS) // 连接超时 60s .readTimeout(120, TimeUnit.SECONDS) // 读取超时 120s .writeTimeout(60, TimeUnit.SECONDS) // 写入超时 60s .build(); } /** * deepseek会话回复接口 */ @FunctionalInterface public interface ReplyFunction { void apply(String reasoningContent, String content); } /** * deepseek会话 * @param question 问题 * @param maxTokens 最大回复量 * @param preMessage 之前的消息，用于多轮会话 * @return * @throws IOException */ public void chat(ReplyFunction replyFunction, String question, int maxTokens, JSONArray preMessage) throws IOException { JSONArray messages = new JSONArray(); if (preMessage != null) { messages.addAll(preMessage); } JSONObject message = new JSONObject(); message.put("role", "user"); message.put("content", question); messages.add(message); JSONObject json = new JSONObject(); json.put("model", deepseekModel); json.put("messages", messages); json.put("max_tokens", maxTokens); json.put("stream", true); RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json.toString()); Request request = new Request.Builder() .url(deepseekHost + CHAT_URL) .addHeader("Authorization", "Bearer " + deepseekApiKey) .post(body).build(); Response response = client.newCall(request).execute(); if (response.code() == 401 || response.code() == 403) { throw new BadRequestAlertException("Unauthorized", "认证失败", ""); } // 获取响应流 InputStream inputStream = response.body().byteStream(); // 包装为BufferedReader，指定编码方式 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); String line; try { // 逐行读取数据 while ((line = reader.readLine()) != null) { String str = line.replace("data:", ""); if (!StringUtils.hasText(str) || "[DONE]".equals(str.trim())) continue; try { JSONObject responseJson = JSONObject.parseObject(str); if (responseJson.containsKey("choices") && !responseJson.getJSONArray("choices").isEmpty()) { JSONObject delta = responseJson.getJSONArray("choices") .getJSONObject(0) .getJSONObject("delta"); // 有些模型思考内容会在reasoning_content中单独返回 String reasoningContent = delta.getString("reasoning_content"); String content = delta.getString("content"); boolean isFinish = "stop".equals(responseJson.getJSONArray("choices").getJSONObject(0).get("finish_reason")); replyFunction.apply(reasoningContent, content); } } catch (Exception e) { log.error(line); } } } catch (IOException e) { log.error("unreadable response", e); } finally { // 确保关闭所有流和资源 try { reader.close(); } catch (IOException e) { log.error("close BufferedReader error", e); } } } }
打开Excel模板编写Java代码实现数据导出 public void getAllMonitorDataAndExport(MonitorDataQueryVM monitorDataQueryVM, HttpServletResponse response) throws IOException { monitorDataQueryVM.setPageNumber(0); monitorDataQueryVM.setPageSize(Integer.MAX_VALUE); Page<MonitorDataDTO> monitorDataDTOPage = getAllMonitorData(monitorDataQueryVM); File file = File.createTempFile("监测数据预警", ".xlsx"); try { // 数据映射 List<Map<String, Object>> mapList = new ArrayList<>(); if (!monitorDataDTOPage.getContent().isEmpty()) { // 查询工点 ProjectDTO site = projectService.findOne(monitorDataQueryVM.getProjectId()).orElse(null); // 查询工区 ProjectDTO area = projectService.findOne(site.getParentId()).orElse(null); for (MonitorDataDTO monitorDataDTO : monitorDataDTOPage.getContent()) { Map<String, Object> row = new HashMap<>(); row.put("area", area.getProjectName()); row.put("site", site.getProjectName()); if (!ObjectUtils.isEmpty(monitorDataDTO.getCollectTime())) { row.put("collectTime", InstantUtil.instantToStr(monitorDataDTO.getCollectTime(), "yyyy/MM/dd")); } else { row.put("collectTime", ""); } if (!StringUtils.isEmpty(monitorDataDTO.getPointType())) { row.put("pointType", MonitorPointType.getByCode(monitorDataDTO.getPointType()).getName()); } else { row.put("pointType", ""); } row.put("pointCode", monitorDataDTO.getPointCode()); if (!StringUtils.isEmpty(monitorDataDTO.getAlarmLevel())) { row.put("alarmLevel", MonitorAlarmLevelEnum.getByCode(monitorDataDTO.getAlarmLevel()).getName()); } else { row.put("alarmLevel", ""); } row.put("currentValue", monitorDataDTO.getCurrentValue()); if ("1".equals(monitorDataDTO.getIsEliminate())) { row.put("isEliminate", "已消警"); } else { row.put("isEliminate", "未消警"); } mapList.add(row); } // 设置表头 Map<String, Object> dataMap = new HashMap<>(); dataMap.put("headers", new String[]{ "工区", "工点", "预警发生日期", "测点类型", "测点名称", "预警级别", "累计值", "是否消警" }); // 模板路径 ClassPathResource classPathResource = new ClassPathResource("/templates/MonitorDataTemplate.xlsx"); try (InputStream inputStream = classPathResource.getInputStream()) { // 创建 Excel 文件 if (!AsposeCellUtil.createExcel(dataMap, mapList, inputStream, file.getAbsolutePath())) { throw new RuntimeException("Excel 文件生成失败"); } } // 将文件输出到浏览器 response.setCharacterEncoding("UTF-8"); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("监测数据预警.xlsx", "UTF-8")); try (InputStream is = new FileInputStream(file.getAbsolutePath()); OutputStream os = response.getOutputStream()) { IOUtils.copy(is, os); } } } catch (Exception e) { log.error("监测数据预警导出错误！", e); throw new BadRequestAlertException("监测数据预警导出错误！", "", HttpStatus.UNAUTHORIZED.getReasonPhrase()); } finally { if (file.exists()) { file.delete(); } } Workbook workbook = new Workbook(); WorksheetCollection worksheets = workbook.getWorksheets(); }AsposeCellUtil.javapublic class AsposeCellUtil { private static License license = null; private final static Logger log = LoggerFactory.getLogger(AsposeCellUtil.class); private static InputStream getWordLicense() { try { InputStream is = AsposeCellUtil.class. getClassLoader().getResourceAsStream("license/license.xml"); return is; } catch (Exception e) { e.printStackTrace(); } return null; } public static boolean getLicense() { boolean result = true; try { InputStream is = getWordLicense(); if (license == null && is != null) { license = new License(); license.setLicense(is); } result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /*public static boolean createExcel(List<Map> mapList, String modelPath, String savePath) throws Exception { Map<String, List<Map>> dataMap = new HashMap(); dataMap.put("data",mapList); return createExcel(dataMap,modelPath,savePath); }*/ public static boolean createExcel(Map<String, Object> dataMap,List<Map<String,Object>> mapList, String modelPath, String savePath) throws Exception { if (!getLicense()) {//必须要验证, 否则有水印 return false; } File modelFile = new File(modelPath); if (!modelFile.exists()) { log.debug("--模板文件不存在--"); return false; } //读取工作簿 Workbook wb = new Workbook(modelPath); //创建Aspose.Cells WorkbookDesigner 的对象 WorkbookDesigner designer = new WorkbookDesigner(); designer.setWorkbook(wb); //设置DataTable对象 if (mapList!=null) { designer.setDataSource("data", new MapCellsTable(mapList)); } //设置普通参数 if (dataMap!=null){ for (Map.Entry<String,Object> mm : dataMap.entrySet()) { designer.setDataSource(mm.getKey(),mm.getValue()); } } //根据数据源处理生成报表内容 designer.process(); //将查询数据写入到文件中 wb.save(savePath,wb.getFileFormat()); return true; } public static boolean createExcel(Map<String, Object> dataMap,List<Map<String,Object>> mapList, InputStream modelFile, String savePath) throws Exception { if (!getLicense()) {//必须要验证, 否则有水印 return false; } if (modelFile == null) { log.debug("--模板文件不存在--"); return false; } //读取工作簿 Workbook wb = new Workbook(modelFile); //创建Aspose.Cells WorkbookDesigner 的对象 WorkbookDesigner designer = new WorkbookDesigner(); designer.setWorkbook(wb); //设置普通参数 // if (dataMap!=null){ // for (Map.Entry<String,Object> mm : dataMap.entrySet()) { // if("columnDataList".equals(mm.getKey())){ // //动态列数据集合 // designer.setDataSource("columns", (Object[])dataMap.get("columnDataList")); // }else{ // designer.setDataSource(mm.getKey(),mm.getValue()); // } // } // } if (dataMap != null) { for (Map.Entry<String, Object> mm : dataMap.entrySet()) { designer.setDataSource(mm.getKey(), mm.getValue()); } } //设置DataTable对象 if (mapList!=null) { designer.setDataSource("data", new MapCellsTable(mapList)); } //设置动态列行数据 // /* // * Set the Variable Array marker to a cell. You may also place this Smart Marker into a template file manually using Excel // * and then open this file via WorkbookDesigner // */ // sheet.getCells().get("A1").putValue("&=$VariableArray"); // // // Set the data source for the marker(s) // report.setDataSource("VariableArray", new String[] { "English", "Arabic", "Hindi", "Urdu", "French" }); // // // Set the CallBack property // report.setCallBack(new SmartMarkerCallBack(report.getWorkbook())); //根据数据源处理生成报表内容 designer.process(); //将查询数据写入到文件中 wb.save(savePath,wb.getFileFormat()); return true; } public static boolean createExcel(Map<String, Object> dataMap,List<Map<String,Object>> mapList, InputStream modelFile, String savePath,int[] mergeColumns) throws Exception { if (!getLicense()) {//必须要验证, 否则有水印 return false; } if (modelFile == null) { log.debug("--模板文件不存在--"); return false; } //读取工作簿 Workbook wb = new Workbook(modelFile); //创建Aspose.Cells WorkbookDesigner 的对象 WorkbookDesigner designer = new WorkbookDesigner(); designer.setWorkbook(wb); //设置普通参数 // if (dataMap!=null){ // for (Map.Entry<String,Object> mm : dataMap.entrySet()) { // if("columnDataList".equals(mm.getKey())){ // //动态列数据集合 // designer.setDataSource("columns", (Object[])dataMap.get("columnDataList")); // }else{ // designer.setDataSource(mm.getKey(),mm.getValue()); // } // } // } if (dataMap != null) { for (Map.Entry<String, Object> mm : dataMap.entrySet()) { designer.setDataSource(mm.getKey(), mm.getValue()); } } //设置DataTable对象 if (mapList!=null) { designer.setDataSource("data", new MapCellsTable(mapList)); } //设置动态列行数据 // /* // * Set the Variable Array marker to a cell. You may also place this Smart Marker into a template file manually using Excel // * and then open this file via WorkbookDesigner // */ // sheet.getCells().get("A1").putValue("&=$VariableArray"); // // // Set the data source for the marker(s) // report.setDataSource("VariableArray", new String[] { "English", "Arabic", "Hindi", "Urdu", "French" }); // // // Set the CallBack property // report.setCallBack(new SmartMarkerCallBack(report.getWorkbook())); //根据数据源处理生成报表内容 designer.process(); //合并单元格 Worksheet sheet = wb.getWorksheets().get(0); for (int j=0; j < mergeColumns.length; j++) { for (int i = 2; i <= mapList.size(); i++) { Cell cell = sheet.getCells().get(i, mergeColumns[j]); // 获取当前单元格 Cell aboveCell = sheet.getCells().get(i - 1, mergeColumns[j]); // 获取上一个单元格 // 检查当前单元格是否为空 if (cell.getStringValue().isEmpty() && !aboveCell.getStringValue().isEmpty()) { // 如果为空，则合并当前单元格和上一个单元格 sheet.getCells().merge(i-1, mergeColumns[j], 2, 1); } } } //将查询数据写入到文件中 wb.save(savePath,wb.getFileFormat()); return true; } /** * 如果有动态列头（需动态生成列头以及对应每行的表格数据） * 参考模板（当行动态表头；删除列）：target\classes\templates\import\主要物资月度需用量计划表.xlsx * 参考模板（多行动态表头；最后一列为动态数据; 合并单元格）： target\classes\templates\import\分工号主要物资设计数量明细表 * dynamic_cell_C-4 动态列头（dataMap中key为dynamic_cell_开头，C-4代表单元格C4；值为 List<String> 标识多列的数据） * dynamic_cell_noinsert_C-4 动态列头不插入列，满足特定模板（dataMap中key为dynamic_cell_noinsert开头，C-4代表单元格C4；值为 List<String> 标识多列的数据） * dynamic_cell_data_C-7-RIGHT 动态列头对应的数据绑定列（dataMap中key为dynamic_cell_data_开头,C-7代表单元格C7,RIGHT 表示文字居右对齐，CENTER表示居左对齐，默认dynamic_cell_data_C-7表示居左对齐） * merge_row_cell_title 合并单元格。 开始行索引：beginRowIndex ； 开始列索引：beginCellIndex ；合并总行数：totalRows；合并总列数：totalColumns * @param dataMap * @param mapList * @param modelFile * @param savePath * @return * @throws Exception */ public static boolean createExcelDynamicColumns(Map<String, Object> dataMap,List<Map<String,Object>> mapList, InputStream modelFile, String savePath) throws Exception { if (!getLicense()) {//必须要验证, 否则有水印 return false; } if (modelFile == null) { System.out.println("--模板文件不存在--"); log.error("模板文件不存在"); return false; } //读取工作簿 Workbook wb = new Workbook(modelFile); Worksheet sheet = wb.getWorksheets().get(0); //创建Aspose.Cells WorkbookDesigner 的对象 WorkbookDesigner designer = new WorkbookDesigner(); designer.setWorkbook(wb); //设置普通参数 if (dataMap!=null){ //设置变量 for (Map.Entry<String,Object> mm : dataMap.entrySet()) { if(mm.getKey().indexOf("dynamic_cell_") != -1 || mm.getKey().indexOf("remove_cell_") != -1){ continue; } designer.setDataSource(mm.getKey(),mm.getValue()); } //删除列 List<Map.Entry<String,Object>> removeCellList = dataMap.entrySet().stream().filter(w->w.getKey().indexOf("remove_cell_") != -1).collect(Collectors.toList()); //设置动态列变量 List<Map.Entry<String,Object>> dynamicCellList = dataMap.entrySet().stream().filter(w->w.getKey().indexOf("dynamic_cell_") != -1).collect(Collectors.toList()); List<Integer> insertColumnIndexList = new ArrayList<>(); List<Map.Entry<String,Object>> noInsertColList = dynamicCellList.stream().filter(w-> w.getKey().indexOf("dynamic_cell_noinsert_") != -1).collect(Collectors.toList());//不插入动态列索引，直接写入列（含添加了动态数据后的列） List<Map.Entry<String,Object>> selDynamicCellList = dynamicCellList.stream().filter(w-> w.getKey().indexOf("dynamic_cell_noinsert_") == -1).collect(Collectors.toList()); Map<String,Integer> noInsertList = noInsertColList.stream().collect(Collectors.toMap(w->(w.getKey().replaceAll("dynamic_cell_noinsert_","").replaceAll("-","").toUpperCase()),b->(Integer) b.getValue())); for (Map.Entry<String,Object> mm :selDynamicCellList) { List<String> cellList = (List<String>)mm.getValue(); String columnKey = mm.getKey(); boolean isRowData = columnKey.indexOf("dynamic_cell_data_") != -1; String keyDataStr = mm.getKey().replaceAll("dynamic_cell_data_","");//行数据 String keyTitleStr = mm.getKey().replaceAll("dynamic_cell_","");//标题数据 String[] keys = isRowData ? keyDataStr.split("-") : keyTitleStr.split("-");//G-6格式，G-6/G-6-CENTER/G-6-RIGHT,居左/居中/居右对齐 String key = keys[0].toUpperCase(); Integer cellNum = Integer.parseInt(keys[1]); String cellKey = key + cellNum; String align = keys.length > 2 ? keys[2] : "";//默认居左对齐 Cell cell = sheet.getCells().get(cellKey); int rowCount = cellNum;//行索引 int cellIndex = cell.getColumn();//列索引 int rowIndex = rowCount - 1; boolean isNoInsert = noInsertList.containsKey(cellKey);//是否不需要插入列，在现有列写入 Integer noInsertColIndex = isNoInsert ? (Integer)noInsertList.get(cellKey) : -1;//是否不需要插入列，在现有列写入索引 for (String cellName:cellList) { if(!isNoInsert || (isNoInsert && noInsertColIndex > cellIndex)){ if (!insertColumnIndexList.contains(cellIndex)) { sheet.getCells().insertColumn(cellIndex);//插入动态列 insertColumnIndexList.add(cellIndex); //扩大删除列 List<Map.Entry<String, Object>> selRemoveColList = removeCellList.stream().filter(w -> (int) w.getValue() >= cellNum.intValue()).collect(Collectors.toList()); if (selRemoveColList != null && selRemoveColList.size() > 0) { for (int i = 0; i < selRemoveColList.size(); i++) { int removeCellNum = (int) selRemoveColList.get(i).getValue(); selRemoveColList.get(i).setValue(++removeCellNum); } } } } if(isRowData){ cell = sheet.getCells().get(rowIndex,cellIndex); if(!StringUtils.isEmpty(align) && align.toUpperCase().equals("RIGHT")){ Style style = cell.getStyle(); style.setHorizontalAlignment(TextAlignmentType.RIGHT); cell.setStyle(style); } else if(!StringUtils.isEmpty(align) && align.toUpperCase().equals("CENTER")){ Style style = cell.getStyle(); style.setHorizontalAlignment(TextAlignmentType.CENTER); cell.setStyle(style); } cell.putValue("&=data."+cellName.replaceAll("\\.","-"));//. 会影响绑定数据，所以替换特殊字符.为其他字符 }else{ Cell titleCell = sheet.getCells().get(rowIndex,cellIndex); titleCell.putValue(cellName); } cellIndex++; } } //删除需要删除的列(删除列需要按小到大顺序添加) if(removeCellList != null && removeCellList.size() > 0){ for(int i = 0; i < removeCellList.size();i++){ int cellNum = (int)removeCellList.get(i).getValue(); sheet.getCells().deleteColumn(cellNum - i); } } } //设置DataTable对象 if (mapList!=null) { designer.setDataSource("data", new MapCellsTable(mapList)); } //设置动态列行数据 // /* // * Set the Variable Array marker to a cell. You may also place this Smart Marker into a template file manually using Excel // * and then open this file via WorkbookDesigner // */ // sheet.getCells().get("A1").putValue("&=$VariableArray"); // // // Set the data source for the marker(s) // report.setDataSource("VariableArray", new String[] { "English", "Arabic", "Hindi", "Urdu", "French" }); // // // Set the CallBack property // report.setCallBack(new SmartMarkerCallBack(report.getWorkbook())); // //自适应行高 // sheet.autoFitRows(); //根据数据源处理生成报表内容 designer.process(); //合并单元格 List<Map.Entry<String,Object>> mergeCellList = dataMap.entrySet().stream().filter(w->w.getKey().indexOf("merge_row_cell_") != -1).collect(Collectors.toList()); for (Map.Entry<String,Object> mm :mergeCellList) { Map<String,Integer> mergeDatas = (Map<String,Integer>)mm.getValue(); Integer beginRowIndex = mergeDatas.get("beginRowIndex");//开始合并行 Integer totalRows = mergeDatas.get("totalRows");//合并结束行 Integer beginCellIndex = mergeDatas.get("beginCellIndex");//开始合并列 Integer totalColumns = mergeDatas.get("totalColumns");//合并结束列 sheet.getCells().merge(beginRowIndex,beginCellIndex,totalRows,totalColumns,true); } //将查询数据写入到文件中 wb.save(savePath,wb.getFileFormat()); return true; } /** * 读取excel * @param file 文件 * @return * @param <T> * @throws Exception */ public static <T extends ExcelColumn> JSONArray readExcel(MultipartFile file) throws Exception { return readExcel(file, new ArrayList<>()); } /** * 读取excel * excel的行列开始下标都是 0 * columns的配，例如： * * * 数据1 * 数据2 * * * 数据3 * 数据4 * 数据5 * * * 数据6 * 数据7 * * * 这样的表格需要配置的 columns 就是: * { columnName: 数据1-数据3, columnKey: xxx-1, columnType: yyy-1 } * { columnName: 数据2-数据4-数据6, columnKey: xxx-2, columnType: yyy-2 } * { columnName: 数据2-数据5-数据7, columnKey: xxx-3, columnType: yyy-3 } * @param file 文件 * @param columns 列配置 * @return * @param <T> * @throws Exception */ public static <T extends ExcelColumn> JSONArray readExcel(MultipartFile file, List<T> columns) throws Exception { return readExcel(file, columns, 0, 0, 0, "-", 1); } /** * 格式化表头以最小粒度的列为基准 * excel的行列开始下标都是 0 * columns的配，例如： * * * 数据1 * 数据2 * * * 数据3 * 数据4 * 数据5 * * * 数据6 * 数据7 * * * 这样的表格需要配置的 columns 就是: * { columnName: 数据1-数据3, columnKey: xxx-1, columnType: yyy-1 } * { columnName: 数据2-数据4-数据6, columnKey: xxx-2, columnType: yyy-2 } * { columnName: 数据2-数据5-数据7, columnKey: xxx-3, columnType: yyy-3 } * @param worksheet 工作表 * @param columns excel列的参数 * @param headerStartRow 表头开始的行 * @param headerEndRow 表头结束的行 * @param headerSplitChar 复杂表头的分隔符 * @return * @param <T> excel列的类型 */ private static <T extends ExcelColumn> List<ExcelColumn> formatHeaders(Worksheet worksheet, List<T> columns, int headerStartRow, int headerEndRow, String headerSplitChar) { // 处理配置项里的表头和字段对应关系 Map<String, T> columnMap = columns.stream().collect(Collectors.toMap(T::getColumnName, Function.identity())); // 获取表头行 List<ExcelColumn> realColumns = new ArrayList<>(); ExcelColumn ec; int maxColumn = worksheet.getCells().getMaxDataColumn(); List<String> headers = new ArrayList<>(); // 处理表头，如果表头有配置，则取配置中的key，如果没有则取表头 for (int column = 0; column <= maxColumn; column++) { headers = new ArrayList<>(); // 遍历每一行到指定的头部行数 for (int row = headerStartRow; row <= headerEndRow; row++) { Cell cell = worksheet.getCells().get(row, column); if (cell.isMerged()) { // 当前行 为合并行的第一行，取他的值为表头 if (cell.getMergedRange().getFirstRow() == row) { headers.add(worksheet.getCells().get(cell.getMergedRange().getFirstRow(), cell.getMergedRange().getFirstColumn()).getStringValue()); } } else { headers.add(cell.getStringValue()); } } String columnName = String.join(headerSplitChar, headers); ec = new ExcelColumn(); if (columnMap.containsKey(columnName) && StringUtils.hasText(columnMap.get(columnName).getColumnKey())) { ec.setColumnKey(columnMap.get(columnName).getColumnKey()); ec.setColumnType(columnMap.get(columnName).getColumnType()); } else { ec.setColumnKey(columnName); } realColumns.add(ec); } return realColumns; } /** * 读取excel * excel的行列开始下标都是 0 * columns的配，例如： * * * 数据1 * 数据2 * * * 数据3 * 数据4 * 数据5 * * * 数据6 * 数据7 * * * 这样的表格需要配置的 columns 就是: * { columnName: 数据1-数据3, columnKey: xxx-1, columnType: yyy-1 } * { columnName: 数据2-数据4-数据6, columnKey: xxx-2, columnType: yyy-2 } * { columnName: 数据2-数据5-数据7, columnKey: xxx-3, columnType: yyy-3 } * @param file 文件 * @param columns 列配置 * @param sheetIndex 第几个工作表 * @param headerStartRow 表头开始的行 * @param headerEndRow 表头结束的行 * @param headerSplitChar 复杂表头之间的拼接符 * @param bodyStartRow 表内容开始行 * @return * @param <T> * @throws Exception */ public static <T extends ExcelColumn> JSONArray readExcel(MultipartFile file, List<T> columns, int sheetIndex, int headerStartRow, int headerEndRow, String headerSplitChar, int bodyStartRow) throws Exception { JSONArray result = new JSONArray(); InputStream is = file.getInputStream(); Workbook workbook = new Workbook(is); Worksheet worksheet = workbook.getWorksheets().get(sheetIndex); // 处理表头 List<ExcelColumn> realColumns = formatHeaders(worksheet, columns, headerStartRow, headerEndRow, headerSplitChar); JSONObject object; ExcelColumn realColumn; for (int row = bodyStartRow; row <= worksheet.getCells().getMaxDataRow(); row++) { object = new JSONObject(); try { // 在行处捕捉异常，失败则整行失败 for (int col = 0; col <= worksheet.getCells().getMaxDataColumn(); col++) { if (realColumns.size() > col) { realColumn = realColumns.get(col); // 获取单元格 Cell cell = worksheet.getCells().get(row, col); // 根据配置不同的类型单独处理，默认是文本 switch (realColumn.getColumnType() == null ? "" : realColumn.getColumnType()) { case "Double": object.put(realColumn.getColumnKey(), cell.getDoubleValue()); break; case "Integer": object.put(realColumn.getColumnKey(), cell.getIntValue()); break; case "Boolean": object.put(realColumn.getColumnKey(), cell.getBoolValue()); break; case "DateTime": object.put(realColumn.getColumnKey(), cell.getDateTimeValue()); break; case "Float": object.put(realColumn.getColumnKey(), cell.getFloatValue()); break; case "BigDecimal": Object cellValue = cell.getValue(); BigDecimal bigDecimalValue; if (cellValue instanceof Double) { bigDecimalValue = BigDecimal.valueOf((Double) cellValue); } else if (cellValue instanceof Integer) { bigDecimalValue = BigDecimal.valueOf((Integer) cellValue); } else if (cellValue instanceof String) { bigDecimalValue = new BigDecimal((String) cellValue); } else { bigDecimalValue = new BigDecimal("0"); } object.put(realColumn.getColumnKey(), bigDecimalValue); break; case "Instant": if (cell.getType() == CellValueType.IS_STRING) { String stringValue = cell.getStringValue(); Instant instantValue = DateUtil.parseInstant(stringValue); object.put(realColumn.getColumnKey(), instantValue); } else if (cell.getType() == CellValueType.IS_DATE_TIME) { if (cell.getDateTimeValue() != null) { Instant instantValue = cell.getDateTimeValue().toUniversalTime().j().toInstant(); object.put(realColumn.getColumnKey(), instantValue); } else { object.put(realColumn.getColumnKey(), null); } } else if (cell.getType() == CellValueType.IS_NUMERIC) { Instant instantValue = Instant.ofEpochMilli((long)cell.getValue()); object.put(realColumn.getColumnKey(), instantValue); } break; case "Percent": String value = cell.getStringValue(); if (StringUtils.isEmpty(value)) { object.put(realColumn.getColumnKey(), 0); } else { try { value = value.replace("%", ""); object.put(realColumn.getColumnKey(), Double.parseDouble(value)); } catch (Exception e) { object.put(realColumn.getColumnKey(), 0); } } break; default: object.put(realColumn.getColumnKey(), cell.getStringValue()); break; } } } result.add(object); } catch (Exception e) { log.error("read excel row: {} error: {}", row, e.getMessage()); throw e; } } return result; } }
打开word模板，通过设置标签的方式占位编写Java代码实现替换word模板中的占位 public void exportChangeDesignRecords(HttpServletResponse response, HttpServletRequest request, String id) { ChangeDesignRecord changeDesignRecord = changeDesignRecordRepository.findById(id).get(); response.setHeader("Cache-Control", "public, max-age=604800"); String fileName= "审批（审核）表.docx"; try(InputStream inputStream = FileUtil.getAsposeTempFileStream(request,fileName,"")) { AsposeUtil.judgeLicense("word"); Document nodes = new Document(inputStream); // nodes.getRange().getBookmarks().get("changeCode").setText(changeDesignRecord.getChangeCode()); nodes.getRange().getBookmarks().get("changeType").setText(changeDesignRecord.getChangeType()); nodes.getRange().getBookmarks().get("contractAmount").setText(changeDesignRecord.getContractAmount().toString()); nodes.getRange().getBookmarks().get("changeAmount").setText(changeDesignRecord.getChangeAmount().toString()); nodes.getRange().getBookmarks().get("changeDescription").setText(changeDesignRecord.getChangeDescription()); nodes.save(response.getOutputStream(), SaveFormat.DOCX); response.getOutputStream().flush(); } catch (Exception e) { log.error("导出{}失败：{}", changeDesignRecord, e); } }FileUtil /** * 获取aspose模板文件流 * @param fileName 文件名 * @param directory 模板目录 为空则默认模板路径 * @return * @throws UnsupportedEncodingException */ public static InputStream getAsposeTempFileStream(HttpServletRequest request, String fileName, String directory) throws IOException { /** 获取模板文件位置**/ String fileDirectory = directory; if (StringUtils.isEmpty(fileDirectory)) { fileDirectory = "templates/import/";//读取classes目录下文件 } fileDirectory = fileDirectory.replaceAll("\\\\","/"); fileDirectory = URLDecoder.decode(fileDirectory, "utf-8"); if(fileDirectory.lastIndexOf("/") != fileDirectory.length() - 1){ fileDirectory += "/"; } InputStream inputStream = FileUtil.class.getClassLoader().getResourceAsStream(fileDirectory + fileName); return inputStream; }
import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; public class IpUtil { public static String getIP(final HttpServletRequest req) throws Exception { if (req == null) { throw (new Exception("getIpAddr method HttpServletRequest Object is null")); } String ipStr = req.getHeader("x-forwarded-for"); if (StringUtils.isEmpty(ipStr) || "unknown".equalsIgnoreCase(ipStr)) { ipStr = req.getHeader("Proxy-Client-IP"); } if (StringUtils.isEmpty(ipStr) || "unknown".equalsIgnoreCase(ipStr)) { ipStr = req.getHeader("WL-Proxy-Client-IP"); } if (StringUtils.isEmpty(ipStr) || "unknown".equalsIgnoreCase(ipStr)) { ipStr = req.getRemoteAddr(); } // 多个路由时，取第一个非unknown的ip final String[] arr = ipStr.split(","); for (final String str : arr) { if (!"unknown".equalsIgnoreCase(str)) { ipStr = str; break; } } // 目的是将localhost访问对应的ip 0:0:0:0:0:0:0:1 转换成 127.0.0.1 return ipStr.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ipStr; } }