java实现文件对比
- 需求
- 实现效果
- 后端代码引入
需求
web项目需要实现文件内容对比功能,开发语言是java,也就是通过java实现类似于svn的文件对比功能
实现效果
效果图如下
后端代码引入
首先引入对比的核心jar包
<!--对比工具依赖-->
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.9</version>
</dependency>
页面流程实现,点击列表页按钮 历史版本 查看历史版本记录
历史版本跳转方法
<li>
<a class="btn btn-info" onclick="getHistoryVersion();" shiro:hasPermission="project:publishCosFiles:history">
<i class="fa fa-history"></i> 历史版本
</a>
</li>
//查看当前目录的历史版本
function getHistoryVersion(){
$.modal.openTab('历史版本',prefix + "/getHistoryVersion?platform="+$('#platform').val()+"&title="+$("#title").val());
}
controller方法,接收页面传参跳转到历史版本列表页面
/**
* 获取目录历史版本页面
*/
@RequestMapping("/getHistoryVersion")
@RequiresPermissions("project:publishCosFiles:history")
public String getHistoryVersion(HttpServletRequest request) throws Exception {
String title = request.getParameter("title");
String platform = request.getParameter("platform");
request.setAttribute("title", title);
request.setAttribute("platform", platform);
return prefix + "/publishCosFileVersion";
}
历史版本列表页面
列表页面代码 publishCosFileVersion.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('文件版本列表')" />
<style>
.my-search-collapse {
width: 100%;
background: #fff;
border-radius: 6px;
margin-top: 10px;
padding-top: 10px;
padding-bottom: 6px;
box-shadow: 1px 1px 3px rgba(0,0,0,.2);
}
</style>
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 my-search-collapse">
<div>
<div style="color:red;">当前目录:[[${title}]]</div>
</div>
</div>
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<input type="hidden" id="path" name="path" th:value="${title}">
<input type="hidden" id="platform" name="platform" th:value="${platform}">
<li>
文件名:<input type="text" name="name"/>
</li>
<li>
<input name="createUser" type="hidden" id="createUser"/>
操作人:<input id="createUserName" name="createUserName" readonly type="text" onclick="selectUserDeptTree('createUser','createUserName')" >
</li>
<li>
备注信息:<input type="text" name="remark"/>
</li>
<li class="input-daterange input-group">
修改时间:
<input type="text" name="createStartDate" readonly id="createStartDate"/>
至
<input type="text" name="createEndDate" readonly id="createEndDate"/>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="search1();"><i
class="fa fa-search"></i> 搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="reset1();"><i
class="fa fa-refresh"></i> 重置</a>
</li>
<li style="float: right">
<button style="width: 40px;" class="btn btn-default btn-outline" type="button" onclick="search1();" name="refresh" aria-label="刷新" title="刷新"><i class="glyphicon glyphicon-refresh icon-refresh"></i> </button>
</li>
</ul>
</div>
</form>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table" data-mobile-responsive="true"></table>
</div>
</div>
</div>
<div th:include="include :: footer"></div>
<script th:inline="javascript">
var viewImgFlag = [[${@permission.hasPermi('project:publishCosFiles:showImage')}]];
var prefix = ctx + "project/publishFileVersion";
$(function() {
var options = {
url: prefix + "/cmsFileVersionList",
modalName: "文件版本",
showExport: false,
pagination: false,
showSearch: false,
showToggle: false,
showRefresh:false,
showColumns:false,
sortName:'name',
sortOrder:'asc',
columns: [
{
title: '',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
//所有的加版本号
actions.push('<a class="" href="javascript:" onclick="addChildHtml(\'' + row.name + '\', this,'+row.id+' )"><i class="glyphicon glyphicon-plus icon-plus"></i></a>');
return actions.join('');
}
},
{
checkbox: true
},
{
field : 'id',
title : '主键id',
visible: false
},
{
field : 'name',
title : '文件名',
formatter: function(value, row, index) {
var showname = value;
if (value.length > 15) {
showname = value.substr(0,14)+"...";
}
var html = "<div title='"+value+"'>"+showname+"</div>";
return html;
},
sortable:true
},
{
field : 'versionNum',
title : '版本号',
},
{
field : 'createDate',
title : '修改时间',
sortable:true
},
{
field : 'size',
title : '文件大小',
formatter: function(value, row, index) {
if(row.delFlag === 1){
// 删除的时候大小展示已删除
return "已删除";
}
var fileSize = row.size;
if (fileSize != null) {
var number1024 = 1<<10;
var number1048576 = 1<<20;
var number1073741824 = 1<<30;
if (fileSize > number1024) {
if (fileSize > number1048576) {
if (fileSize > number1073741824) {
var fixed = (fileSize/number1073741824).toFixed(2);
return fixed+"GB";
}else {
var fixed = (fileSize/number1048576).toFixed(2);
return fixed+"MB";
}
}else {
var fixed = (fileSize/number1024).toFixed(2);
return fixed+"KB";
}
}else {
return fileSize+"B";
}
}else {
return "-";
}
}
},
{
field : 'createUserName',
title : '修改人',
},
{
field : 'remark',
title : '备注信息',
formatter: function(value, row, index) {
return $.table.tooltip(value);
}
},
{
title: '操作',
align: 'left',
cellStyle: function (value, row, index) {
return {css: {"overflow": "hidden", "text-overflow": "ellipsis", "white-space": "nowrap"}}
},
formatter: function(value, row, index) {
var actions = [];
var fileType = row.type;
if (fileType != null
&& ("jpg" == fileType.toLowerCase() || "png" == fileType.toLowerCase() || "gif" == fileType.toLowerCase() || "jpeg" == fileType.toLowerCase())) {
actions.push('<a class="btn btn-success btn-xs ' + viewImgFlag + '" href="javascript:void(0)" onclick="viewImage(\'' + row.bakPathPrefix +"/"+ row.bakName +'\')"><i class="fa fa-leaf"></i>预览</a> ');
}
return actions.join('');
}
}]
};
$.table.init(options);
initLaydate();
});
/*账户管理-新增-选择人部门树*/
function selectUserDeptTree(treeId,treeName) {
// url 可选参数, 其他参数 请自定扩展
// permission 权限 0.无权限 1.按照登录人数据权限
// showType 展示方式 0.人员 1.人员+工号
// checkType 选中类型 0.多选 1.单选
// showLevel 展示层级
var url = ctx + "system/userDeptTree?showType=1&showLevel=2&checkType=1";
var options = {
title: '选择部门',
width: "500",
url: url,
callBack: function (index, layero) {
// 是否允许选父级
var body = layer.getChildFrame('body', index);
$("#"+treeId).val(body.find('#ids').val());
$("#"+treeName).val(body.find('#names').val());
layer.close(index);
}
};
$.modal.openOptions(options);
}
function initLaydate() {
layui.use('laydate', function() {
var laydate = layui.laydate;
var startDate = laydate.render({
elem: '#createStartDate',
//max: $('#createEndDate').val(),
type: 'datetime',
theme: 'molv',
trigger: 'click'
/*done: function (value, date) {
// 结束时间大于开始时间
if (value !== '') {
endDate.config.min.year = date.year;
endDate.config.min.month = date.month - 1;
endDate.config.min.date = date.date;
} else {
endDate.config.min.year = '';
endDate.config.min.month = '';
endDate.config.min.date = '';
}
}*/
});
var endDate = laydate.render({
elem: '#createEndDate',
//max:$.common.getNowFormatDate(),
type: 'datetime',
theme: 'molv',
trigger: 'click'
/*done: function (value, date) {
// 开始时间小于结束时间
if (value !== '') {
startDate.config.max.year = date.year;
startDate.config.max.month = date.month - 1;
startDate.config.max.date = date.date;
} else {
startDate.config.max.year = '';
startDate.config.max.month = '';
startDate.config.max.date = '';
}
}*/
});
})
}
//预览图片
function viewImage(filePath) {
var url = ctx + 'project/publishCosFiles/showImage?filePath=' + filePath;
$.modal.open("图片预览", url , '600', '500',function (index) {
layer.close(index);
});
}
//加载当前文件的历史版本
function addChildHtml(name,tdThis,id) {
// 判断是否是加号,加号的话展开并加载子类,如果是减号的话则删除掉子类
if ($(tdThis).children('i').hasClass('glyphicon-plus')) {
// 加载子类数据
var html = "";
var formData = {"path":$("#path").val(),"platform":$("#platform").val(),"name":name};
$.ajax({
url: prefix + "/getOneFileHistory",
type: 'post',
dataType: "json",
data: formData,
success: function(result) {
if(result.code == web_status.SUCCESS){
$.each(result.data,function (i,k) {
html += '<tr child-index="'+i+'" class="child-tr pid-'+id+'">';
html +='<td style="text-align: center; "/>';
html +='<td/>';
if(k.name==null){
html +='<td style="">-</td>';
}else {
var showname = k.name;
if (showname.length > 15) {
showname = showname.substr(0,14)+"...";
}
html += '<td title="'+k.name+'">'+showname+'</td>';
}
html +='<td style="">'+k.versionNum+'</td>';
html +='<td style="">'+k.createDate+'</td>';
var fileSize = k.size;
if (fileSize != null) {
var number1024 = 1<<10;
var number1048576 = 1<<20;
var number1073741824 = 1<<30;
if (fileSize > number1024) {
if (fileSize > number1048576) {
if (fileSize > number1073741824) {
var fixed = (fileSize/number1073741824).toFixed(2);
html +='<td style="">'+fixed+'GB</td>';
}else {
var fixed = (fileSize/number1048576).toFixed(2);
html +='<td style="">'+fixed+'MB</td>';
}
}else {
var fixed = (fileSize/number1024).toFixed(2);
html +='<td style="">'+fixed+'KB</td>';
}
}else {
html +='<td style="">'+fileSize+'B</td>';
}
}else {
html +='<td style="">-</td>';
}
html +='<td style="">'+k.createUserName+'</td>';
html +='<td style="">'+$.table.tooltip(k.remark);+'</td>';
html +='<td style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; ">';
var fileType = k.type;
if (fileType != null
&& ("jpg" == fileType.toLowerCase() || "png" == fileType.toLowerCase() || "gif" == fileType.toLowerCase() || "jpeg" == fileType.toLowerCase())) {
html +='<a class="btn btn-success btn-xs ' + viewImgFlag + '" href="javascript:void(0)" onclick="viewImage(\'' + k.bakPathPrefix +"/"+ k.bakName +'\')"><i class="fa fa-leaf"></i>预览</a> ';
}
if(i!=0){
//不是第一个的时候加还原
html +='<a class="btn btn-warning btn-xs " href="javascript:void(0)" onclick="rollbackFile('+k.id+')">';
html +='<i class="fa fa-mail-reply"/>还原</a> ';
}else {
// 是第一个的时候如果是删除的加还原
if(k.delFlag === 1){
html +='<a class="btn btn-warning btn-xs " href="javascript:void(0)" onclick="rollbackFile('+k.id+')">';
html +='<i class="fa fa-mail-reply"/>还原</a> ';
}
}
if (fileType != null && ("html" == fileType.toLowerCase() || "shtml" == fileType.toLowerCase() || "css" == fileType.toLowerCase() ||
"js" == fileType.toLowerCase()|| "txt" == fileType.toLowerCase())) {
html +='<a class="btn btn-info btn-xs " href="javascript:void(0)" onclick="comparedFile('+k.id+')">';
html +='<i class="fa fa-exchange"/>对比</a>';
}
html +='</td>';
html +='</tr>';
});
$(tdThis).parent().parent().after(html);
}else{
if("未登录或登录超时。请重新登录"==res.msg){
window.location=ctx+"login";
}else {
$.modal.alertError(result.msg);
}
}
}
});
// 写入之类成功之后在去掉加号等样式,防止出错
$(tdThis).children('i').removeClass('glyphicon-plus');
$(tdThis).children('i').removeClass('icon-plus');
$(tdThis).children('i').addClass('glyphicon-minus');
$(tdThis).children('i').addClass('icon-minus');
}else {
// 减号的时候点击将子类数据移除,然后将减号变加号
$('.pid-'+id+'').remove();
$(tdThis).children('i').removeClass('glyphicon-minus');
$(tdThis).children('i').removeClass('icon-minus');
$(tdThis).children('i').addClass('glyphicon-plus');
$(tdThis).children('i').addClass('icon-plus');
}
}
//还原文件的方法
function rollbackFile(id){
$.modal.confirm("确认要还原该文件吗?", function() {
var url = prefix + "/rollbackFile";
var data = { "id": id };
$.operate.submit(url, "post", "json", data,function () {
$.table.search();
});
});
}
//对比选择
function comparedFile(id){
$.modal.openTab('对比选择',prefix + "/selectCompared/?id="+id);
}
function search1() {
var start = $("#createStartDate").val();
var end = $("#createEndDate").val();
var startdate = new Date(start);
var enddate = new Date(end);
if (startdate.getTime() > enddate.getTime()) {
$.modal.alertError("开始时间不能大于结束时间");
return false;
}
$.table.search();
}
function reset1() {
$("#createUser").val("");
$.form.reset();
}
</script>
</body>
</html>
点击列表页之后跳转文件对比controller方法
@RequestMapping("/comparedThisFile")
public String comparedThisFile(HttpServletRequest request, ModelMap mmap)
{
String text1path = null;
String text2path = null;
//String htmlPath = null;
String platform = request.getParameter("platform");
try {
String leftId = request.getParameter("leftId");
String rightId = request.getParameter("rightId");
String profile = ConfigConstant.cosTempPath;
PublishFileVersion left = publishFileVersionService.selectPublishFileVersionById(Long.parseLong(leftId));
// 获取左边文件路径
String bakpath = left.getBakPathPrefix();
String bakname = left.getBakName();
//file.text_0
String filename = left.getName();
String downname = filename +"_"+left.getVersionNum();
if (filename.contains(".")) {
downname = filename.replace(".","_"+left.getVersionNum()+".");
}
if(left.getObfuscateFlag()==1){
// 混淆的时候文件名用混淆的那个名字
bakname = left.getObfuscateSourceName();
}
String leftkey = bakpath + bakname;
PublishFileVersion right = publishFileVersionService.selectPublishFileVersionById(Long.parseLong(rightId));
// 获取右边文件路径
String bakpath2 = right.getBakPathPrefix();
String bakname2 = right.getBakName();
//file.text_0
String filename2 = right.getName();
String downname2 = filename2 +"_"+right.getVersionNum();
if (filename2.contains(".")) {
downname2 = filename2.replace(".","_"+right.getVersionNum()+".");
}
if(right.getObfuscateFlag()==1){
// 混淆的时候文件名用混淆的那个名字
bakname2 = right.getObfuscateSourceName();
}
String rightkey = bakpath2 + bakname2;
if (Constants.Platform.COS.getValue().equals(Integer.valueOf(platform))) {
CosClientUtil cosClientUtil = new CosClientUtil();
try {
//下载到服务器
text1path = profile + downname;
cosClientUtil.download(leftkey, text1path);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
//下载到服务器
text2path = profile + downname2;
cosClientUtil.download(rightkey, text2path);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
text1path = bakpath+File.separator+bakname;
text2path = bakpath2+File.separator+bakname2;
}
List<String> diffString = DiffHandleUtils.diffString(text1path,text2path,downname,downname2);
//System.out.println("======diff::::::"+diffString);
//在服务器生成一个diff.html文件,打开便可看到两个文件的对比
//htmlPath = profile + "compareresult.html";
String string = DiffHandleUtils.generateDiffString(diffString);
//System.out.println("======res::::::"+string);
mmap.put("right", string);
}catch (Exception e) {
e.printStackTrace();
}finally {
if (Constants.Platform.COS.getValue().equals(Integer.valueOf(platform))) {
//删除下载的临时文件
if (StringUtils.isNotEmpty(text1path)) {
boolean delete = new File(text1path).delete();
}
if (StringUtils.isNotEmpty(text2path)) {
boolean delete = new File(text2path).delete();
}
}
}
return prefix+"/newComparedFile";
}
文件对比工具类主要方法 DiffHandleUtils.java
package com.dongao.project.utils;
import com.github.difflib.UnifiedDiffUtils;
import com.github.difflib.patch.Patch;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @Author zhangw
* @Date 2022/4/21 18:04
* @Version 1.0
*/
public class DiffHandleUtils {
/**
* 对比两文件的差异,返回原始文件+diff格式
*
* @param original 原文件内容
* @param revised 对比文件内容
*/
public static List<String> diffString(List<String> original, List<String> revised) {
return diffString(original, revised, null, null);
}
/**
* 对比两文件的差异,返回原始文件+diff格式
*
* @param original 原文件内容
* @param revised 对比文件内容
* @param originalFileName 原始文件名
* @param revisedFileName 对比文件名
*/
public static List<String> diffString(List<String> original, List<String> revised, String originalFileName, String revisedFileName) {
originalFileName = originalFileName == null ? "原始文件" : originalFileName;
revisedFileName = revisedFileName == null ? "对比文件" : revisedFileName;
//两文件的不同点
Patch<String> patch = com.github.difflib.DiffUtils.diff(original, revised);
//生成统一的差异格式
List<String> unifiedDiff = UnifiedDiffUtils.generateUnifiedDiff(originalFileName, revisedFileName, original, patch, 0);
if (unifiedDiff.size() == 0) {
//如果两文件没差异则插入如下
unifiedDiff.add("--- " + originalFileName);
unifiedDiff.add("+++ " + revisedFileName);
unifiedDiff.add("@@ -0,0 +0,0 @@");
} else if (unifiedDiff.size() >= 3 && !unifiedDiff.get(2).contains("@@ -1,")) {
//如果第一行没变化则插入@@ -0,0 +0,0 @@
unifiedDiff.add(2, "@@ -0,0 +0,0 @@");
}
//原始文件中每行前加空格
List<String> original1 = original.stream().map(v -> " " + v).collect(Collectors.toList());
//差异格式插入到原始文件中
return insertOrig(original1, unifiedDiff);
}
/**
* 对比两文件的差异,返回原始文件+diff格式
*
* @param filePathOriginal 原文件路径
* @param filePathRevised 对比文件路径
*/
public static List<String> diffString(String filePathOriginal, String filePathRevised,String originalName,
String revisedName) {
//原始文件
List<String> original = null;
//对比文件
List<String> revised = null;
File originalFile = new File(filePathOriginal);
File revisedFile = new File(filePathRevised);
try {
original = Files.readAllLines(originalFile.toPath());
revised = Files.readAllLines(revisedFile.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return diffString(original, revised, originalName, revisedName);
}
/**
* 通过两文件的差异diff生成 html文件,打开此 html文件便可看到文件对比的明细内容
*
* @param diffString 调用上面 diffString方法获取到的对比结果
* @param htmlPath 生成的html路径,如:/user/var/mbos/ent/21231/diff.html
* HTML输出接受一个Javascript对象,该对象可能有以下配置项:
*
* inputFormat: 输入数据的格式: 'diff' 或者 'json', 默认是'diff'
* outputFormat: 输出数据的格式: 'line-by-line' 或者 'side-by-side', 默认是'line-by-line'
* showFiles: 在对比之前查看文件列表,true 或者false,默认是false
* matching: 匹配level: 'lines'用于匹配行, 'words' 用于匹配行和单词,或者设置为'none',默认为none
* matchWordsThreshold: 单词相似度下限, 默认是0.25
* matchingMaxComparisons: 为了匹配一组变化最多执行的比较次数,默认是2500
* maxLineLengthHighlight: 如果行数小于此值,则仅仅执行差异突出显示,默认是10000
* templates: 使用预备好的编译的模板替换部分html的对象。
* rawTemplates: 具有原始未编译模板的对象替换部分html。
* 更多参考 https://github.com/rtfpessoa/diff2html/tree/master/src/templates
*/
public static void generateDiffHtml(List<String> diffString, String htmlPath) {
StringBuilder builder = new StringBuilder();
for (String line : diffString) {
builder.append(line);
builder.append("\n");
}
String template = "<!DOCTYPE html>\n" +
"<html lang=\"en-us\">\n" +
" <head>\n" +
" <meta charset=\"utf-8\" />\n" +
" <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/styles/github.min.css\" />\n" +
" <link rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css\" />\n" +
" <script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js\"></script>\n" +
" </head>\n" +
" <script>\n" +
" const diffString = `\n" +
"temp\n" +
"`;\n" +
"\n" +
"\n" +
" document.addEventListener('DOMContentLoaded', function () {\n" +
" var targetElement = document.getElementById('diffElement');\n" +
" var configuration = {\n" +
" drawFileList: true,\n" +
" fileListToggle: false,\n" +
" fileListStartVisible: false,\n" +
" fileContentToggle: false,\n" +
" matching: 'words',\n" +
" outputFormat: 'side-by-side',\n" +
" synchronisedScroll: true,\n" +
" highlight: true,\n" +
" renderNothingWhenEmpty: true,\n" +
" };\n" +
" var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);\n" +
" diff2htmlUi.draw();\n" +
" diff2htmlUi.highlightCode();\n" +
" });\n" +
" </script>\n" +
" <body>\n" +
" <div id=\"diffElement\"></div>\n" +
" </body>\n" +
"</html>";
String string = builder.toString();
string = string.replace("/", "\\/");
template = template.replace("temp", string);
FileWriter f = null; //文件读取为字符流
try {
f = new FileWriter(htmlPath);
BufferedWriter buf = new BufferedWriter(f); //文件加入缓冲区
buf.write(template); //向缓冲区写入
buf.close(); //关闭缓冲区并将信息写入文件
f.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过两文件的差异diff生成 string,打开此 html文件便可看到文件对比的明细内容
* @param diffString
* @return
*/
public static String generateDiffString(List<String> diffString) {
StringBuilder builder = new StringBuilder();
builder.append("`");
for (String line : diffString) {
//对比页面开始符号冲突
if (line.contains("`")) {
line = line.replace("`","\\`");
}
//</script>结束标志</冲突
if (line.contains("</")) {
line = line.replace("</", "<\\/");
}
//正则\冲突
if (line.contains("\\") && line.contains("^")) {
line = line.replace("\\", "\\\\");
}
//页面取值冲突 ${}
if (line.contains("${")) {
line = line.replace("${", "$\\{");
}
builder.append(line);
builder.append("\n");
}
builder.append("`");
String string = builder.toString();
return string;
}
//统一差异格式插入到原始文件
public static List<String> insertOrig(List<String> original, List<String> unifiedDiff) {
List<String> result = new ArrayList<>();
//unifiedDiff中根据@@分割成不同行,然后加入到diffList中
List<List<String>> diffList = new ArrayList<>();
List<String> d = new ArrayList<>();
for (int i = 0; i < unifiedDiff.size(); i++) {
String u = unifiedDiff.get(i);
if (u.startsWith("@@") && !"@@ -0,0 +0,0 @@".equals(u) && !u.contains("@@ -1,")) {
List<String> twoList = new ArrayList<>();
twoList.addAll(d);
diffList.add(twoList);
d.clear();
d.add(u);
continue;
}
if (i == unifiedDiff.size() - 1) {
d.add(u);
List<String> twoList = new ArrayList<>();
twoList.addAll(d);
diffList.add(twoList);
d.clear();
break;
}
d.add(u);
}
//将diffList和原始文件original插入到result,返回result
for (int i = 0; i < diffList.size(); i++) {
List<String> diff = diffList.get(i);
List<String> nexDiff = i == diffList.size() - 1 ? null : diffList.get(i + 1);
//含有@@的一行
String simb = i == 0 ? diff.get(2) : diff.get(0);
String nexSimb = nexDiff == null ? null : nexDiff.get(0);
//插入到result
insert(result, diff);
//解析含有@@的行,得到原文件从第几行开始改变,改变了多少(即增加和减少的行)
Map<String, Integer> map = getRowMap(simb);
if (null != nexSimb) {
Map<String, Integer> nexMap = getRowMap(nexSimb);
int start = 0;
if (map.get("orgRow") != 0) {
start = map.get("orgRow") + map.get("orgDel") - 1;
}
int end = nexMap.get("revRow") - 2;
//插入不变的
insert(result, getOrigList(original, start, end));
}
if (simb.contains("@@ -1,") && null == nexSimb) {
insert(result, getOrigList(original, 0, original.size() - 1));
} else if (null == nexSimb && map.get("orgRow") < original.size()) {
insert(result, getOrigList(original, map.get("orgRow"), original.size() - 1));
}
}
return result;
}
//将源文件中没变的内容插入result
public static void insert(List<String> result, List<String> noChangeContent) {
for (String ins : noChangeContent) {
result.add(ins);
}
}
//解析含有@@的行得到修改的行号删除或新增了几行
public static Map<String, Integer> getRowMap(String str) {
Map<String, Integer> map = new HashMap<>();
if (str.startsWith("@@")) {
String[] sp = str.split(" ");
String org = sp[1];
String[] orgSp = org.split(",");
//源文件要删除行的行号
map.put("orgRow", Integer.valueOf(orgSp[0].substring(1)));
//源文件删除的行数
map.put("orgDel", Integer.valueOf(orgSp[1]));
String[] revSp = org.split(",");
//对比文件要增加行的行号
map.put("revRow", Integer.valueOf(revSp[0].substring(1)));
map.put("revAdd", Integer.valueOf(revSp[1]));
}
return map;
}
//从原文件中获取指定的部分行
public static List<String> getOrigList(List<String> original1, int start, int end) {
List<String> list = new ArrayList<>();
if (start <= end && end < original1.size()) {
for (; start <= end; start++) {
list.add(original1.get(start));
}
}
return list;
}
}
返回页面展示结果如开始,newComparedFile.html 页面代码
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" th:href="@{/css/github.min.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/css/diff2html.min.css}"/>
<script type="text/javascript" th:src="@{/js/diff2html-ui.min.js}"></script>
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
</head>
<script>
const diffString = [(${right})];
document.addEventListener('DOMContentLoaded', function () {
var targetElement = document.getElementById('diffElement');
var configuration = {
//在diff之前显示文件列表:true或false,默认值为true
drawFileList: true,
//允许切换文件摘要列表:true或false,默认值为true
fileListToggle: false,
//选择文件摘要列表是否开始可见:true或false,默认值为false
fileListStartVisible: false,
//允许切换每个文件内容:true或false,默认值为true
fileContentToggle: false,
//匹配level: 'lines'用于匹配行, 'words' 用于匹配行和单词,或者设置为'none',默认为none
matching: 'words',
//输出数据的格式: 'line-by-line' 或者 'side-by-side', 默认是'line-by-line'
outputFormat: 'side-by-side',
synchronisedScroll: true,
highlight: true,
renderNothingWhenEmpty: true,
};
var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
diff2htmlUi.draw();
diff2htmlUi.highlightCode();
var texts = $(".d2h-code-wrapper").find("td[class='d2h-info']").find("div");
if (texts.length) {
$.each(texts,function (index,value) {
var _this = $(this);
//console.log(_this.text())
_this.text("");
});
}
});
</script>
<body>
<div id="diffElement"></div>
</body>
</html>
为了更方便大家测试,这里我补充一个测试类CompareTest.java代码,方便大家测试
package com.dongao;
import java.io.*;
import java.util.List;
/**
* @Description TODO
* @Author P001
* @Date 2022/4/21 16:37
* @Version 1.0
*/
class CompareTest {
//E:\data\webapps\wwwroot_release\dazx\ei_bak\costemp
private static String path =
"E:\\data\\webapps\\wwwroot_release\\dazx\\ei_bak\\costemp\\test"+System.currentTimeMillis()+".html";
public static void main(String[] args) throws IOException {
/*DiffContent.diff();
ShowDiff.show(path);*/
//对比 F:\n1.txt和 F:\n2.txt 两个文件,获得不同点
List<String> diffString = DiffHandleUtils.diffString("E:\\data\\webapps\\wwwroot_release\\dazx\\ei_bak" +
"\\costemp\\main0.html",
"E:\\data\\webapps\\wwwroot_release\\dazx\\ei_bak\\costemp\\main1.html");
//在F盘生成一个diff.html文件,打开便可看到两个文件的对比
DiffHandleUtils.generateDiffHtml(diffString,path);
System.out.println("success");
}
}
标签:function,文件,java,String,List,html,var,对比 From: https://blog.51cto.com/u_10917175/5925142