java文本对比工具简单封装
功能描述
由于项目中使用nacos做配置中心,测试环境和生产环境经常出现配置不同导致程序问题。为避免以后此类事情出现,所以要做一个文本对比小工具。
把工程打包成 可执行 jar,mian方法运行,执行完毕自动打开浏览器输出对比文本内容。
参考上一篇文章: https://www.cnblogs.com/meow-world/articles/15209517.html
目录:
1.创建maven工程
2.创建入口main方法类
3.读取main方法参数中外部配置文件
4.读取nacos测试和生产配置文件
5.对比文本内容
6.展示对比后有差异的文本
目录结构
1.创建maven工程
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>aicloud.com</groupId> <artifactId>DiffTools</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.9.0</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-core</artifactId> <version>5.7.10</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.74</version> </dependency> <!--对比工具依赖--> <dependency> <groupId>io.github.java-diff-utils</groupId> <artifactId>java-diff-utils</artifactId> <version>4.9</version> </dependency> </dependencies> </project>
2.创建入口main方法类
package diff; import diff.nacos.DiffContent; import diff.nacos.ReadLocalConfig; import diff.nacos.ReadResources; import diff.nacos.ShowDiff; import java.io.IOException; /** * @date 2021/8/27 */ public class TestMain { private static String path = "D:/temp/test.html"; public static void main(String[] args) throws IOException { String configPath = args[0]; ReadLocalConfig.readLocalConfig(configPath); ReadResources.readNacosConfig(); DiffContent.diff(); ShowDiff.show(path); } }
3.读取main方法参数中外部配置文件
public class ReadLocalConfig { public static String nacosTestUrl; public static String nacosTestNameSpaceId; public static String nacosProdUrl; public static String nacosProdNameSpaceId; public static void readLocalConfig(String configPath) throws IOException { File file = new File(configPath); InputStream inputStream = new FileInputStream(file); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String next; while ((next = bufferedReader.readLine()) != null) { if (StrUtil.isNotBlank(next)) { int index = next.indexOf(":") + 1; String substring = next.substring(index).trim(); if (next.startsWith("nacosTestUrl")) { nacosTestUrl = substring; } if (next.startsWith("nacosTestNameSpaceId")) { nacosTestNameSpaceId = substring; } if (next.startsWith("nacosProdUrl")) { nacosProdUrl = substring; } if (next.startsWith("nacosProdNameSpaceId")) { nacosProdNameSpaceId = substring; } } } if (StrUtil.isBlank(nacosTestUrl)) { throw new RuntimeException("配置文件地址错误"); } } }
4.读取nacos测试和生产配置
package diff.nacos; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; import diff.nacos.model.NacosPerConfig; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import static diff.nacos.ReadLocalConfig.*; /** * 读取nacos命令空间下的内容 * @author wangzhihui * @date 2021/8/27 */ public class ReadResources { //读取到的配置问价集合 //key dataId : value: 文本内容 public static Map<String, List<String>> configTestMap = new HashMap<>(); public static Map<String, List<String>> configProdMap = new HashMap<>(); public static OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); public static void readNacosConfig() throws IOException { List<NacosPerConfig> testList = doRequest(nacosTestUrl, nacosTestNameSpaceId); List<NacosPerConfig> prodList = doRequest(nacosProdUrl, nacosProdNameSpaceId); testList.forEach(item -> configTestMap.put(item.getDataId(), Arrays.asList(item.getContent().split("\n")))); prodList.forEach(item -> configProdMap.put(item.getDataId(), Arrays.asList(item.getContent().split("\n")))); }
private static List<NacosPerConfig> doRequest(String url, String nameSpaceId) throws IOException { HttpUrl.Builder builder = HttpUrl.parse(url).newBuilder(); builder.addQueryParameter("dataId", null); builder.addQueryParameter("group", null); builder.addQueryParameter("appName", null); builder.addQueryParameter("config_tags", null); builder.addQueryParameter("pageNo", "1"); builder.addQueryParameter("pageSize", "50"); builder.addQueryParameter("tenant", nameSpaceId); builder.addQueryParameter("search", "accurate"); Request request = new Request.Builder() .url(builder.build()) .get() .build(); Response response = okHttpClient.newCall(request).execute(); String jsonStr = response.body().string(); JSONObject jsonObject = JSON.parseObject(jsonStr, JSONObject.class); List<NacosPerConfig> list = jsonObject.getObject("pageItems", new TypeReference<List<NacosPerConfig>>() { }); return list; } }
5.对比文本内容
import cn.hutool.core.collection.CollectionUtil; import com.github.difflib.DiffUtils; import com.github.difflib.patch.AbstractDelta; import com.github.difflib.patch.Chunk; import com.github.difflib.patch.Patch; import java.util.*; public class DiffContent { public static Map<String, List<String>> ERROR_MAP = new HashMap<>(); public static void diff() { ReadResources.configTestMap.forEach((k, original) -> { List<String> revised = ReadResources.configProdMap.get(k); if (CollectionUtil.isEmpty(revised)) { ERROR_MAP.put(k, Arrays.asList("--- 缺失")); } if (CollectionUtil.isNotEmpty(revised)) { diffDetail(revised, original, k); } }); } public static void diffDetail(List<String> original, List<String> revised, String dataId) { Patch<String> diff = DiffUtils.diff(original, revised); List<AbstractDelta<String>> deltas = diff.getDeltas(); List<String> errorList = new LinkedList<>(); deltas.forEach(delta -> { switch (delta.getType()) { case INSERT: //新增 Chunk<String> insert = delta.getTarget(); int position = insert.getPosition(); String errorInsert = "+++ " + (position + 1) + " " + insert.getLines(); errorList.add(errorInsert); break; case CHANGE://修改 Chunk<String> source = delta.getSource(); Chunk<String> target1 = delta.getTarget(); String errorChange = "+-- " + (source.getPosition() + 1) + " " + source.getLines() + "\n+ " + "" + (target1.getPosition() + 1) + " " + target1.getLines(); errorList.add(errorChange); break; case DELETE: //删除 Chunk<String> delete = delta.getSource(); String errorDelete = "--- " + (delete.getPosition() + 1) + " " + delete.getLines(); errorList.add(errorDelete); break; case EQUAL: break; } }); ERROR_MAP.put(dataId, errorList); } }
6.展示对比后有差异的文本
import cn.hutool.core.collection.CollectionUtil; import java.awt.*; import java.io.*; import java.net.URI; import java.nio.charset.StandardCharsets; public class ShowDiff { public static void show(String path) throws IOException {
//打开默认浏览器 if (Desktop.isDesktopSupported()) { writeData(path); URI uri = URI.create(path); Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.BROWSE)) { desktop.browse(uri); } } } private static void writeData(String path) throws IOException { File file = createFile(path); FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8); osw.write(appendHtmlData()); osw.flush(); } @SuppressWarnings("all") private static File createFile(String path) throws IOException { File file = new File(path); if (!file.exists()) { file.createNewFile(); } return file; } private static String appendHtmlData() { StringBuilder builder = new StringBuilder(); builder.append(htmlPrefix); builder.append("<h1>一、字符说明</h1>"); builder.append("<ul>") .append("<li>").append(deltaInsert).append(":新增").append("</li>") .append("<li>").append(deltaChange).append(":修改").append("</li>") .append("<li>").append(deltaDelete).append(":删除").append("</li>") .append("</ul>"); builder.append("<h1>二、错误列表</h1>"); DiffContent.ERROR_MAP.forEach((k, errorList) -> { if (CollectionUtil.isNotEmpty(errorList)) { builder.append("<p style=\"color:#FF0000\">").append(k).append("</p>"); errorList.forEach(error -> { builder.append(error).append("<br><br>"); }); } }); builder.append(htmlSuffix); return builder.toString(); } private final static String htmlPrefix = "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n" + "<meta charset=\"utf-8\">\n" + "<title>测试/生产配置diff</title>\n" + "</head>\n" + "<body>"; private final static String htmlSuffix = "</body>\n" + "</html>"; private final static String deltaInsert = "+++"; private final static String deltaChange = "+--"; private final static String deltaDelete = "---"; }
touch fish