项目中碰到需要在导入过程中和当前目录中的图片进行比较,判断是否存在相同的图片,相同则把导入的图片删除掉
该内容较多:需要仔细分析每部分代码,结合你需要内容获取对应代码 !!!!!!!!
首先把工具类导入进来:
对应hash比较工具类我是参考该作者博客:java通过哈希比较图片相似度_java中imagecompare-CSDN博客
我在当前我功能中也有做额外拓展,各位兄弟按自己需求选择使用 !!!!!!!!!
import java.awt.Graphics;
import java.awt.Image;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.util.Arrays;
/**
* 均值哈希实现图像指纹比较
*
* @author
*/
public class ContrastImgUntil {
/**
* 图像指纹的尺寸,将图像resize到指定的尺寸,来计算哈希数组
*/
private static final int HASH_SIZE = 16;
/**
* 保存图像指纹的二值化矩阵
*/
private final byte[] binaryzationMatrix;
public ContrastImgUntil(byte[] hashValue) {
if (hashValue.length != HASH_SIZE * HASH_SIZE)
throw new IllegalArgumentException(String.format("length of hashValue must be %d", HASH_SIZE * HASH_SIZE));
this.binaryzationMatrix = hashValue;
}
public ContrastImgUntil(String hashValue) {
this(toBytes(hashValue));
}
/**
* 本次调用的方法,
*
* @param src
*/
public ContrastImgUntil(BufferedImage src) {
this(hashValue(src));
}
//
private static byte[] hashValue(BufferedImage src) {
BufferedImage hashImage = resize(src, HASH_SIZE, HASH_SIZE);
byte[] matrixGray = (byte[]) toGray(hashImage).getData().getDataElements(0, 0, HASH_SIZE, HASH_SIZE, null);
return binaryzation(matrixGray);
}
/**
* 从压缩格式指纹创建{@link ContrastImgUntil}对象
*
* @param compactValue
* @return
*/
public static ContrastImgUntil createFromCompact(byte[] compactValue) {
return new ContrastImgUntil(uncompact(compactValue));
}
public static boolean validHashValue(byte[] hashValue) {
if (hashValue.length != HASH_SIZE)
return false;
for (byte b : hashValue) {
if (0 != b && 1 != b) return false;
}
return true;
}
public static boolean validHashValue(String hashValue) {
if (hashValue.length() != HASH_SIZE)
return false;
for (int i = 0; i < hashValue.length(); ++i) {
if ('0' != hashValue.charAt(i) && '1' != hashValue.charAt(i)) return false;
}
return true;
}
public byte[] compact() {
return compact(binaryzationMatrix);
}
/**
* 指纹数据按位压缩
*
* @param hashValue
* @return
*/
private static byte[] compact(byte[] hashValue) {
byte[] result = new byte[(hashValue.length + 7) >> 3];
byte b = 0;
for (int i = 0; i < hashValue.length; ++i) {
if (0 == (i & 7)) {
b = 0;
}
if (1 == hashValue[i]) {
b |= 1 << (i & 7);
} else if (hashValue[i] != 0)
throw new IllegalArgumentException("invalid hashValue,every element must be 0 or 1");
if (7 == (i & 7) || i == hashValue.length - 1) {
result[i >> 3] = b;
}
}
return result;
}
/**
* 压缩格式的指纹解压缩
*
* @param compactValue
* @return
*/
private static byte[] uncompact(byte[] compactValue) {
byte[] result = new byte[compactValue.length << 3];
for (int i = 0; i < result.length; ++i) {
if ((compactValue[i >> 3] & (1 << (i & 7))) == 0)
result[i] = 0;
else
result[i] = 1;
}
return result;
}
/**
* 字符串类型的指纹数据转为字节数组
*
* @param hashValue
* @return
*/
private static byte[] toBytes(String hashValue) {
hashValue = hashValue.replaceAll("\\s", "");
byte[] result = new byte[hashValue.length()];
for (int i = 0; i < result.length; ++i) {
char c = hashValue.charAt(i);
if ('0' == c)
result[i] = 0;
else if ('1' == c)
result[i] = 1;
else
throw new IllegalArgumentException("invalid hashValue String");
}
return result;
}
/**
* 缩放图像到指定尺寸1
*
* @param src
* @param width
* @param height
* @return
*/
private static BufferedImage resize(Image src, int width, int height) {
BufferedImage result = new BufferedImage(width, height,
BufferedImage.TYPE_3BYTE_BGR);
Graphics g = result.getGraphics();
try {
g.drawImage(src.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
} finally {
g.dispose();
}
return result;
}
/**
* 计算均值
*
* @param src
* @return
*/
private static int mean(byte[] src) {
long sum = 0;
// 将数组元素转为无符号整数
for (byte b : src) sum += (long) b & 0xff;
return (int) (Math.round((float) sum / src.length));
}
/**
* 二值化处理1
*
* @param src
* @return
*/
private static byte[] binaryzation(byte[] src) {
byte[] dst = src.clone();
int mean = mean(src);
for (int i = 0; i < dst.length; ++i) {
// 将数组元素转为无符号整数再比较
dst[i] = (byte) (((int) dst[i] & 0xff) >= mean ? 1 : 0);
}
return dst;
}
/**
* 转灰度图像1
*
* @param src
* @return
*/
private static BufferedImage toGray(BufferedImage src) {
if (src.getType() == BufferedImage.TYPE_BYTE_GRAY) {
return src;
} else {
// 图像转灰
BufferedImage grayImage = new BufferedImage(src.getWidth(), src.getHeight(),
BufferedImage.TYPE_BYTE_GRAY);
new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null).filter(src, grayImage);
return grayImage;
}
}
@Override
public String toString() {
return toString(true);
}
/**
* @param multiLine 是否分行
* @return
*/
public String toString(boolean multiLine) {
StringBuffer buffer = new StringBuffer();
int count = 0;
for (byte b : this.binaryzationMatrix) {
buffer.append(0 == b ? '0' : '1');
if (multiLine && ++count % HASH_SIZE == 0)
buffer.append('\n');
}
return buffer.toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ContrastImgUntil) {
return Arrays.equals(this.binaryzationMatrix, ((ContrastImgUntil) obj).binaryzationMatrix);
} else
return super.equals(obj);
}
/**
* 与指定的压缩格式指纹比较相似度
*
* @param compactValue
* @return
* @see #compare(ContrastImgUntil)
*/
public float compareCompact(byte[] compactValue) {
return compare(createFromCompact(compactValue));
}
/**
* @param hashValue
* @return
* @see #compare(ContrastImgUntil)
*/
public float compare(String hashValue) {
return compare(new ContrastImgUntil(hashValue));
}
/**
* 与指定的指纹比较相似度
*
* @param hashValue
* @return
* @see #compare(ContrastImgUntil)
*/
public float compare(byte[] hashValue) {
return compare(new ContrastImgUntil(hashValue));
}
/**
* 与指定图像比较相似度
*
* @param image2
* @return
* @see #compare(ContrastImgUntil)
*/
public float compare(BufferedImage image2) {
return compare(new ContrastImgUntil(image2));
}
/**
* 比较指纹相似度
*
* @param src
* @return
* @see #compare(byte[], byte[])
*/
public float compare(ContrastImgUntil src) {
if (src.binaryzationMatrix.length != this.binaryzationMatrix.length)
throw new IllegalArgumentException("length of hashValue is mismatch");
return compare(binaryzationMatrix, src.binaryzationMatrix);
}
/**
* 判断两个数组相似度,数组长度必须一致否则抛出异常
*
* @param f1
* @param f2
* @return 返回相似度(0.0 ~ 1.0)
*/
private static float compare(byte[] f1, byte[] f2) {
if (f1.length != f2.length)
throw new IllegalArgumentException("mismatch ContrastImgUntil length");
int sameCount = 0;
for (int i = 0; i < f1.length; ++i) {
if (f1[i] == f2[i]) ++sameCount;
}
return (float) sameCount / f1.length;
}
public static float compareCompact(byte[] f1, byte[] f2) {
return compare(uncompact(f1), uncompact(f2));
}
public static float compare(BufferedImage image1, BufferedImage image2) {
return new ContrastImgUntil(image1).compare(new ContrastImgUntil(image2));
}
//返回当前图片的哈希值
public String getHashAsString() {
StringBuilder hashStringBuilder = new StringBuilder();
for (byte b : binaryzationMatrix) {
hashStringBuilder.append(b == 0 ? '0' : '1');
}
return hashStringBuilder.toString();
}
}
主要逻辑代码如下:(为了方便解读,后面会具体说明方法的使用解析)
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yr.common.util.*;
import com.yr.dataAnnotations.config.ResourceAccessProperties;
import com.yr.dataAnnotations.config.SpringBootPlusProperties;
import com.yr.dataAnnotations.entity.*;
import com.yr.dataAnnotations.mapper.*;
import com.yr.dataAnnotations.param.*;
import com.yr.dataAnnotations.service.*;
import com.yr.dataAnnotations.util.CmdUtil;
import com.yr.dataAnnotations.util.ContrastImgUntil;
import com.yr.dataAnnotations.vo.*;
import com.yr.newframe.core.common.exception.BusinessException;
import com.yr.newframe.core.common.service.impl.BaseServiceImpl;
import com.yr.newframe.core.config.properties.FrameProperties;
import com.yr.newframe.core.context.UserContext;
import com.yr.newframe.core.core.pagination.PageInfo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yr.newframe.core.core.util.RedisFrameUtil;
import com.yr.newframe.core.util.UUIDUtil;
import org.apache.commons.io.FileUtils;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.awt.image.BufferedImage;
import java.io.*;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
//added by lhh
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Service
public class FileServiceImpl extends BaseServiceImpl<FileMapper, File> implements FileService {
// SIMILARITY_THRESHOLD 定义相似度阈值,例如1表示至少100%相似度才认为图片相同
private static final float SIMILARITY_THRESHOLD = 1f;
/**
* 文件分片上传处理
* @param request
* @param response
* @return
* @throws Exception
*/
public Map<String, String> uploadFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, String> map = new HashMap<>();
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile multipartFile = multipartRequest.getFile("file"); //获得文件分片数据
String uuid = multipartRequest.getParameter("uuid"); //前端uuid,作为标识
int total = Integer.parseInt(multipartRequest.getParameter("totalChunks")); //总片数
int index = Integer.parseInt(multipartRequest.getParameter("chunkNumber")); //分片第几片
String fileName = multipartRequest.getParameter("filename").replaceAll(" ", ""); //获取文件名
String fileType = multipartRequest.getParameter("fileType"); //文件类型:.图片文件
String catalogId = multipartRequest.getParameter("catalogId"); //文件类型,传图片文件目录id
String name = fileName.replace(".zip", "").replaceAll(".tar", "").replace(".gz", "");
if (StrUtil.isBlank(fileType) || StrUtil.equals(fileType, "1")) {
if (StrUtil.isBlank(catalogId)) {
throw new BusinessException("图片文件目录id不能为空!");
}
/** 目录是否关联任务;是:不允许导入图,反之导入 */
QueryWrapper<TaskCatalog> taskCatalogQW = new QueryWrapper<>();
taskCatalogQW.eq("catalog_id", catalogId);
int count = taskCatalogService.count(taskCatalogQW);
if (count > 0) {
throw new BusinessException("该目录已关联任务,不能导入图片!");
}
} else {
throw new BusinessException("文件类型不存在!");
}
//本地测试可启用
// String uploadPath = "D:\\Desktop\\imageTestFile" + java.io.File.separator + CmdUtil.FOLDER_NAME_2 + uuid;//(本地测试)
String uploadPath = springBootPlusProperties.getUploadPath() + CmdUtil.FOLDER_NAME_2 + uuid;
String newName = DateUtil.ymdHms.format(new Date()) + "_" + name + "_" + index + ".tem";
java.io.File uploadFile = new java.io.File(uploadPath, newName);
if (!uploadFile.getParentFile().exists()) {
uploadFile.getParentFile().mkdirs();
}
try {
if (index < total) {
//表示当前分片文件上传成功
multipartFile.transferTo(uploadFile);
map.put("status", "201");
} else {
//表示全部分片文件上传成功
multipartFile.transferTo(uploadFile);
map.put("status", "200");
map.put("uuid", uuid);
map.put("fileName", fileName);
map.put("catalogId", catalogId);
map.put("fileType", fileType);
map.put("id", id);
System.out.println("上传分片文件总数:--------" + total);
}
System.out.println("上传分片文件存储路径:--------" + uploadPath);
} catch (IOException e) {
//分片文件上传失败
e.printStackTrace();
map.put("status", "502");
}
return map;
}
/**
* 合并文件处理方法
* @param fileMergeParam
* @param userId
* @return
* @throws Exception
*/
public Map<String, String> mergeNewProject(FileMergeParam fileMergeParam, String userId) throws Exception {
String uuid = fileMergeParam.getUuid();
String fileName = fileMergeParam.getFileName();
String fileType = fileMergeParam.getFileType(); //文件类型:1.图片文件
String catalogId = fileMergeParam.getCatalogId(); //文件类型为1时,传图片文件目录iD
AlgorithmType algorithmTypeInfo = null;
Map<String, String> map = new HashMap<>();//返回异常
// String uploadPath ="D:\\Desktop\\imageTestFile\\";//(本地测试,修改成本地的文件夹路径)
/** 根路径 */
String uploadPath = springBootPlusProperties.getUploadPath();
/** 压缩包绝对路径;例如:/xxxx/xxxxm/upload/xxx.zip */
java.io.File targetFile;
/** 压缩包名称 */
String name;
try {
/** 压缩包存储的目录地址 */
String newUploadPath;
//缓存的临时分片文件夹
String tempPath = uploadPath + CmdUtil.FOLDER_NAME_2 + uuid;
System.out.println("--------开始分片文件合并--------:" + tempPath);
java.io.File dirFile = new java.io.File(tempPath);
if (!dirFile.exists()) {
throw new BusinessException("文件不存在!");
}
/** 分片上传的文件已经位于同一个文件夹下,方便寻找和遍历(当文件数大于十的时候记得排序,确保顺序是正确的)*/
java.io.File[] files = dirFile.listFiles();
List<java.io.File> fileList = FileUtil.fileSort(files);
if (StrUtil.isBlank(fileType) || StrUtil.equals(fileType, "1")) {
//创建空的合并文件目录
newUploadPath = uploadPath + CmdUtil.FOLDER_NAME_2 + CmdUtil.FOLDER_NAME_3;
name = DateUtil.ymdHms.format(new Date()) + "_" + fileName.replaceAll(" ", "");
targetFile = new java.io.File(newUploadPath, name);
if (!targetFile.getParentFile().exists()) {
targetFile.getParentFile().mkdirs();
}
} else {
throw new BusinessException("文件类型不存在!");
}
System.out.println("合并文件存储路径:--------" + targetFile.getPath());
RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw");
long position = 0;
for (java.io.File file : fileList) {
//每个分片文件的具体路径
RandomAccessFile readFile = new RandomAccessFile(file, "rw");
int chunkSize = 1024 * 3;
byte[] buf = new byte[chunkSize];
writeFile.seek(position);
int byteCount;
while ((byteCount = readFile.read(buf)) != -1) {
if (byteCount != chunkSize) {
byte[] tempBytes = new byte[byteCount];
System.arraycopy(buf, 0, tempBytes, 0, byteCount);
buf = tempBytes;
}
writeFile.write(buf);
position = position + byteCount;
}
readFile.close();
//删除缓存的临时分片文件(本地测试时,需要注释改代码)
FileUtils.deleteQuietly(file);
}
writeFile.close();
//清除缓存的临时分片文件夹(本地测试时,需要注释改代码)
FileUtils.deleteQuietly(dirFile);
} catch (Exception e) {
e.printStackTrace();
map.put("status", "502");
map.put("tipMess", "导入失败!");
return map;
}
System.out.println("--------分片文件合并完成--------");
if (StrUtil.isBlank(fileType) || StrUtil.equals(fileType, "1")) {
/** 图片压缩文件 */
String imageMess = fileNewProjectDispose(targetFile.getPath(), catalogId, StrUtil.isBlank(userId) ? "admin" : userId);
map.put("imageMessage", imageMess);
map.put("status", "200");
map.put("tipMess", "导入成功!");
} else {
throw new BusinessException("文件类型不存在!");
}
return map;
}
/**
* 解压处理图片比较逻辑
* @param path
* @param catalogId
* @param userId
* @return
*/
private String fileNewProjectDispose(String path, String catalogId, String userId) {
String resuletMess = "";
/** 获取目录地址 */
Catalog catalogInfo = catalogService.getById(catalogId);
if (catalogInfo == null) {
throw new BusinessException("目录不存在!");
}
/** 获取该目录下的所有文件,并转成Map用于下面的校验 */
Map<String, File> mapFileInfo = new HashMap<>();
QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
fileQueryWrapper.eq("catalog_id", catalogId);
List<File> fileList = super.list(fileQueryWrapper);
if (fileList.size() > 0) {
for (File vo : fileList) {
String name = vo.getFileName();
if (mapFileInfo.containsKey(name)) {
continue;
}
mapFileInfo.put(name, vo);
mapFileInfo.put(name.substring(0, name.lastIndexOf(".") + 1) + "xml", vo);
}
}
System.out.println("--------开始压缩包解压-------- HT1");
java.io.File filePath = new java.io.File(path);
String folderName = filePath.getName().substring(0, filePath.getName().indexOf("."));
//解压后的地址
String tagert = springBootPlusProperties.getUploadPath() + CmdUtil.FOLDER_NAME_2 + CmdUtil.FOLDER_NAME_3 + folderName + java.io.File.separator;
// String tagert = "D:\\Desktop\\imageTestFile\\img"; //(本地测试)先默认写到本地
java.io.File targertFile = new java.io.File(tagert);
if (!targertFile.exists()) {
targertFile.mkdirs();
}
String deCompressedAddress = ""; //解压后图片存放的路径
try {
/** 解压上传的文件 */
deCompressedAddress = unZip(path, tagert);
System.out.println("解压地址----------HT4_XIAN_ZAI_JIE_YA_HOU_DE" + tagert);
} catch (IOException e) {
System.out.println("文件解压失败----------" + e.getMessage());
throw new BusinessException("文件解压失败!");
}
System.out.println("解压地址----------HT5_YUAN_DI_ZI" + tagert);
/** 原数据库存储图片数据、hash值集合 格式: image.jpg,hash值 */
Map<String, String> imageHash = new HashMap<>();
/** 最新解压导入文件图片数据、hash值集合 格式: image.jpg,hash值 */
Map<String, String> importImgHashValue = new HashMap<>();
try {
/**
* 获取出当前目录下的所有图片地址信息
*/
QueryWrapper<File> queryFileWrapper = new QueryWrapper<>();
queryFileWrapper.eq("catalog_id", catalogId);
List<File> filesData = super.list(queryFileWrapper);
Long catalogInfoId = catalogInfo.getId(); //获取目录id
for (File filesDatum : filesData) {
//图片地址实例:/huidate/hui_ware/data/kistm/file/images/upload/catalog/XX/image.jpg
String imageFile = springBootPlusProperties.getUploadPath() + "catalog" + java.io.File.separator +
catalogInfoId + java.io.File.separator + filesDatum.getFileName();
String hashValue = filesDatum.getHashValue();//导入图片的hash值
imageHash.put(filesDatum.getFileName(), hashValue);
}
System.out.println("原数据图片Map集合内容(DatabasImageMap):" + imageHash);
/** 解压上传的文件拿到解压出来的图片信息*/
List<String> targetPathList = unZipAndGetImagePaths(path, tagert);
/**
* 计算获取出所有导入图片的hash值
* Map集合格式为:<图片名称,hash值>
*/
importImgHashValue = getImportImgHashValue(targetPathList);
if (!imageHash.isEmpty()) {
/** 根据导入图片的hash值和原图片hash值做对比 */
DuplicateResult duplicateResult = compareImagesWithBaseline(imageHash, importImgHashValue);
//重复图片数量
int duplicatesCount = duplicateResult.getDuplicatesCount();
//重复图片集合
List<String> allDuplicates = duplicateResult.getAllDuplicates();
// 如果所有图片都是重复的,则直接返回提示信息,无需进一步处理
if (duplicatesCount == importImgHashValue.size()) {
String tipMessError = "所有导入的数据均为重复数据,已停止处理!";
resuletMess = tipMessError;
return resuletMess; // 这里结束方法的执行
}
//返回的提示
if (duplicatesCount > 0) {
String tipMessError = "当前导入数据存在:" + duplicatesCount + "张重复数据,保留原始数据,重复数据已剔除!";
resuletMess = tipMessError;
}
/**
* 遍历取出重复图片名称,找到解压文件夹,执行删除操作
* deCompressedAddress 解压后的文件路径
*/
Path deCompressDir = Paths.get(deCompressedAddress);
try (DirectoryStream<Path> stream = Files.newDirectoryStream(deCompressDir)) {
// 将解压目录下的文件路径与重复图片名称列表比对,执行删除操作
for (Path fileRemovePath : stream) {
for (String imageName : allDuplicates) {
if (fileRemovePath.getFileName().toString().equals(imageName)) {
// 找到了同名文件,执行删除操作
Files.delete(fileRemovePath);
System.out.println("Deleted: " + fileRemovePath);
break; // 删除后跳出当前图片名称的内层循环,避免重复处理
}
}
}
} catch (IOException e) {
System.err.println("删除文件时发生错误: " + e.getMessage());
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
/** 目录下所有文件(包含子级下文件)路径集合,格式:<文件夹(目录)名称,<文件名称,文件路径集合>> */
Map<String, Map<String, List<String>>> filePathMap = new HashMap<>();
/**
* 拿解压后的目录地址,进行写入图片内容到数据库
*/
java.io.File file = new java.io.File(deCompressedAddress);
FileUtil.findFileList(file, filePathMap);
/** 文件信息存储对象集合 */
List<File> fileInfoList = new ArrayList<>();
String cpCmd;
String mvCmd;
if (CmdUtil.checkSystem()) {
//Windows命令
cpCmd = CmdUtil.WINDOWS_COPY;
mvCmd = CmdUtil.WINDOWS_MOVE;
} else {
//Linux命令
cpCmd = CmdUtil.LINUX_COPY;
mvCmd = CmdUtil.LINUX_MOVE;
}
//解析出图片信息,进行写表操作
Iterator<Map.Entry<String, Map<String, List<String>>>> it = filePathMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Map<String, List<String>>> catalogEntry = it.next();
String catalogKey = catalogEntry.getKey();
Map<String, List<String>> catalogValue = catalogEntry.getValue();
Iterator<Map.Entry<String, List<String>>> itt = catalogValue.entrySet().iterator();
while (itt.hasNext()) {
/** 文件信息存储对象 */
File fileInfo = new File();
fileInfo.setCatalogId(catalogInfo.getId() + "");
fileInfo.setRevision(0);
fileInfo.setCreatedBy(userId);
fileInfo.setCreatedTime(new Date());
fileInfo.setUpdatedBy(userId);
fileInfo.setUpdatedTime(new Date());
int labelNum = 0;
boolean isPic = false;
String hms = DateUtil.hms.format(new Date());
Map.Entry<String, List<String>> entry = itt.next();
String key = entry.getKey();
List<String> filePathList = entry.getValue();
/**
* 遍历出新解压导入文件图片数据、hash值集合,写入图片的hash到当前数据上
*/
importImgHashValue.forEach((imgKey, value) -> {
// 假设文件名是键的一部分,去除扩展名 ".jpg"
String fileNameWithoutExt = imgKey.substring(0, imgKey.length() - 4);
if (key.equals(fileNameWithoutExt)) {
fileInfo.setHashValue(value);
}
});
for (String str : filePathList) {
java.io.File fileStr = new java.io.File(str);
String fileName = fileStr.getName().replaceAll(" ", "");
String name = fileName.substring(0, fileName.indexOf("."));
String suffix = fileName.substring(fileName.indexOf("."));
/** 是否有图片,有则保存数据反之跳过 */
if (filePathList.size() > 1 || ImageUtil.isPic2(suffix)) {
/** 图片或xml文件是否已存在,存在则重命名 */
if (mapFileInfo.containsKey(fileName)) {
name += "-副本" + hms;
fileName = name + suffix;
String parentFilePath = fileStr.getParentFile().getPath() + java.io.File.separator + fileName;
java.io.File newFileStr = new java.io.File(parentFilePath);
if (!fileStr.renameTo(newFileStr)) {
throw new BusinessException("图片或xml文件重命名失败!");
}
fileStr = newFileStr;
}
if (ImageUtil.isPic2(suffix)) {
System.out.println("--------开始图片解析--------");
/** 是图片:1.进行图片解析并获取宽高、拍摄时间。2.生成图片缩略图,并存储 */
fileInfo.setFileName(fileName);
try {
Map<String, Object> imgInfo = ImageUtil.getImgInfo(fileStr);
fileInfo.setWidth((Integer) imgInfo.get("imageWidth"));
fileInfo.setHeigth((Integer) imgInfo.get("imageHeight"));
fileInfo.setFileTime(DateUtil.StrToShortDate(imgInfo.get("shootTime") + ""));
} catch (Exception e) {
System.out.println("图片解析失败---------:" + e.getMessage());
throw new BusinessException("图片解析失败!");
}
String imgThumbPath = springBootPlusProperties.getUploadPath() + CmdUtil.FOLDER_NAME_4 + catalogId + java.io.File.separator + name + "_thumb" + suffix;
//本地测试可先注释
// String imgThumbPath = " D:\\Desktop\\imageTestFile\\" + CmdUtil.FOLDER_NAME_4 + catalogId + java.io.File.separator + name + "_thumb" + suffix;
java.io.File newFile = new java.io.File(imgThumbPath);
if (!newFile.getParentFile().exists()) {
newFile.getParentFile().mkdirs();
}
String copyThumbCommand = cpCmd + " " + fileStr + " " + imgThumbPath;
if (!CmdUtil.sendInstructions(copyThumbCommand)) {
throw new BusinessException("缩略图归类失败!");
}
//本地测试可先注释
if (!ImageUtil.createThumbnail(imgThumbPath, 400, 400)) {
throw new BusinessException("缩略图生成失败!");
}
fileInfo.setThumb(imgThumbPath);
isPic = true;
} else if (StrUtil.equals(suffix, ".xml")) {
System.out.println("--------开始xml解析--------");
/** 是xml文件:进行xml解析并获取内容 */
try {
Map<String, Object> xmlMap = FileUtil.analysisXML(fileStr);
labelNum = (int) xmlMap.get("labelNum");
fileInfo.setLabelNames(xmlMap.get("name") + "");
fileInfo.setHashValue(xmlMap.get("hashValue") + "");
fileInfo.setXmlContent(JSON.toJSONString(xmlMap.get("bndbox")));
} catch (Exception e) {
System.out.println("xml解析失败---------:" + e.getMessage());
throw new BusinessException("xml解析失败!");
}
} else {
continue;
}
fileInfo.setLabelNum(labelNum);
/** 使用Linux命令,将图片和xml移动到相应的目录下 */
String copyCommand = mvCmd + " " + fileStr + " " + catalogInfo.getFilepath();
if (!CmdUtil.sendInstructions(copyCommand)) {
throw new BusinessException("文件归类失败!");
}
System.out.println("--------文件归类成功--------");
}
}
/** 是否有图片,有则保存数据反之跳过 */
if (!isPic) {
continue;
}
String names = fileInfo.getFileName();
if (!mapFileInfo.containsKey(names)) {
mapFileInfo.put(names, fileInfo);
mapFileInfo.put(names.substring(0, names.lastIndexOf(".") + 1) + "xml", fileInfo);
}
fileInfoList.add(fileInfo);
}
}
if (fileInfoList.size() <= 0) {
throw new BusinessException("该压缩包没有文件!");
}
boolean stateBy = super.saveBatch(fileInfoList);
if (!stateBy) {
resuletMess += "保存导入图片失败!请联系管理员!";
}
return resuletMess;
}
/**
* 解压zip文件
*
* @param zipfile 文件地址 D:\\upload\\解压\\a.zip
* @param tagert 解压目的地址 D:\\upload\\解压\\
* @throws IOException
*/
public String unZip(String zipfile, String tagert) throws IOException {
try {
long startTime = System.currentTimeMillis();
File zfile = new File(zipfile);
FileInputStream fis = new FileInputStream(zfile);
String code = "UTF-8";
try {
code = resolveCode(zipfile);
} catch (Exception e) {
e.printStackTrace();
}
//默认UTF-8
Charset charset = Charset.forName(code);
// 输入源zip路径
ZipInputStream zis = new ZipInputStream(fis, charset);
ZipEntry entry = null;
File file = new File(tagert);
if (!file.exists()) {
file.mkdir();
}
String s = null;
BufferedOutputStream bos = null;
while ((entry = zis.getNextEntry()) != null) {
/** 去除文件名的空格和括号 */
String name = entry.getName().replaceAll("\\s*", "").replaceAll("\\(", "").replaceAll("\\)", "");
if (entry.isDirectory()) {
File filePath = new File(tagert + name);
//如果目录不存在,则创建
if (!filePath.exists()) {
filePath.mkdirs();
}
} else {
//当压缩包中包含目录时,程序会当成文件执行,所以添加下面代码进行控制
if (String.valueOf(entry).contains("/")) {
String s1 = name.substring(0, name.lastIndexOf("/"));
String s2 = name.substring(name.lastIndexOf("/") + 1);
File filePath = new File(tagert + s1 + File.separator);
if (!filePath.exists()) {
filePath.mkdir();
}
s = s1 + File.separator;
name = s1 + File.separator + s2;
}
System.err.println(tagert + name.trim());
FileOutputStream fos = new FileOutputStream(tagert + name.trim());
bos = new BufferedOutputStream(fos);
byte buf[] = new byte[1024];
int len;
while ((len = zis.read(buf)) != -1) {
bos.write(buf, 0, len);
}
zis.closeEntry();
//关闭的时候会刷新
bos.close();
}
}
//当压缩包存在下级目录时
if (StrUtil.isNotBlank(s)) {
tagert += s;
}
zis.close();
long endTime = System.currentTimeMillis();
System.out.println("解压完成!所需时间为:" + (endTime - startTime) + "ms");
} catch (IOException e) {
e.printStackTrace();
return "";
}
return tagert;
}
/**
* 拿到解压出来的图片信息
*
* @param zipfile
* @param target
* @return
* @throws IOException
*/
public List<String> unZipAndGetImagePaths(String zipfile, String target) throws IOException {
String resultPath = ZipUtil.unZip(zipfile, target); // 先调用原有解压方法
if (resultPath.isEmpty()) {
System.err.println("解压失败,无法获取图片数据");
return new ArrayList<>(); // 返回一个空列表
}
java.io.File targetFolder = new java.io.File(resultPath);
// 遍历解压目录,查找图片文件,并收集图片路径
List<String> imagePaths = new ArrayList<>();
if (targetFolder.exists() && targetFolder.isDirectory()) {
java.io.File[] files = targetFolder.listFiles((dir, name) -> {
String lowercaseName = name.toLowerCase();
for (String extension : new String[]{"jpg", "jpeg", "png", "gif", "bmp"}) {
if (lowercaseName.endsWith("." + extension)) {
return true;
}
}
return false;
});
if (files != null) {
System.out.println("找到的图片文件如下:");
for (java.io.File imageFile : files) {
// 收集图片的绝对路径
String imagePath = imageFile.getAbsolutePath();
imagePaths.add(imagePath);
// 这里只是打印文件名和大小作为示例,你可以根据需要处理这些图片数据
System.out.println("图片名: " + imageFile.getName() + ", 大小: " + Files.size(imageFile.toPath()) + " bytes");
}
} else {
System.out.println("解压目录下没有找到图片文件");
}
} else {
System.err.println("目标路径不存在或不是一个目录");
}
return imagePaths;
}
/**
* 根据导入的图片,生成对应的图片hash值
*
* @param targetPathList
*/
private Map<String, String> getImportImgHashValue(List<String> targetPathList) {
// 存储结果的Map
Map<String, String> imageHashMap = new HashMap<>();
try {
for (String imagePath : targetPathList) {
// 从路径中提取图片名称
String imageName = new java.io.File(imagePath).getName();
try {
// 读取图片
BufferedImage image = ImageIO.read(new java.io.File(imagePath));
// 计算哈希值
ContrastImgUntil contrastImgUtil = new ContrastImgUntil(image);
String hashString = contrastImgUtil.getHashAsString(); // 假设这个方法已正确实现
// 将图片名与哈希值存入Map
imageHashMap.put(imageName, hashString);
} catch (IOException e) {
System.err.println("Error reading or processing image: " + imagePath);
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
// 打印结果Map
for (Map.Entry<String, String> entry : imageHashMap.entrySet()) {
System.out.println("Image Name: " + entry.getKey() + ", Hash Value: " + entry.getValue());
}
return imageHashMap;
}
/**
* 根据哈希值比较图片是否相同
*
* @param baseImageHashes 原数据库图片的哈希值集合,键为图片名,值为哈希值
* @param imageHashes 待比较图片的哈希值集合,键为图片名,值为哈希值(最新解压导入文件图片数据、hash值集合)
*/
public DuplicateResult compareImagesWithBaseline(Map<String, String> baseImageHashes, Map<String, String> imageHashes) {
if (baseImageHashes == null || imageHashes == null) {
throw new IllegalArgumentException("图片哈希值集合不能为空");
}
int duplicateCount = 0;
List<String> duplicates = new ArrayList<>();
// 遍历待比较图片的哈希值集合
for (Map.Entry<String, String> entry : imageHashes.entrySet()) {
String imageName = entry.getKey();
String imageHash = entry.getValue();
// 在原数据库图片哈希值集合中查找是否有相同的哈希值
if (baseImageHashes.containsValue(imageHash)) {
duplicates.add(imageName);
duplicateCount++;
}
}
return new DuplicateResult(duplicateCount, duplicates);
}
/**
* 根据hash比较图片是否相同
*
* @param baseImagePaths 原数据库图片数据
* @param imagePaths 导人解压出来的图片数据
*/
// public DuplicateResult compareImagesWithBaseline(List<String> baseImagePaths, List<String> imagePaths) {
// if (baseImagePaths == null || imagePaths == null) {
// throw new IllegalArgumentException("图片路径集合不能为空");
// }
// Map<String, List<String>> duplicatesMap = new HashMap<>(); // 使用Map存储重复图片路径
// int duplicateCount = 0; // 引入计数器变量
//
// // 对于每一张导入的图片,与原图集合中的所有图片进行比较
// for (String baseImagePath : baseImagePaths) {
// try {
// BufferedImage baseImage = ImageIO.read(new java.io.File(baseImagePath));
// ContrastImgUntil baseComparator = new ContrastImgUntil(baseImage);
//
// for (String imagePath : imagePaths) {
// try {
// BufferedImage imgToCompare = ImageIO.read(new java.io.File(imagePath));
// ContrastImgUntil comparator = new ContrastImgUntil(imgToCompare);
//
// float similarity = baseComparator.compare(comparator);
//
// // 处理相似度结果
// if (similarity == SIMILARITY_THRESHOLD) {
// // 注意这里交换了basePath和imagePath的位置以更准确地反映哪两张图片重复
// String errorMsg = String.format("导入图片 '%s' 与原图 '%s' 相似度为100%%,存在重复!", imagePath, baseImagePath);
// System.err.println(errorMsg + "分隔符!!!");
// duplicateCount++; // 每发现一次重复就增加计数
// duplicatesMap.computeIfAbsent(baseImagePath, k -> new ArrayList<>()).add(imagePath);
// }
// } catch (IOException e) {
// e.printStackTrace(); // 处理解码图片时可能发生的异常
// } catch (Exception ice) {
// System.err.println(ice.getMessage());
// }
// }
// } catch (IOException e) {
// e.printStackTrace(); // 处理解码图片时可能发生的异常
// } catch (Exception ice) {
// System.err.println(ice.getMessage());
// }
// }
// // 将所有重复图片路径合并到一个列表中
// List<String> allDuplicates = new ArrayList<>();
// for (List<String> group : duplicatesMap.values()) {
// allDuplicates.addAll(group);
// }
//
// // 返回重复图片的数量和重复图片
// return new DuplicateResult(duplicateCount, allDuplicates);
// }
解压方法解读:
/**
* 解压zip文件
*
* @param zipfile 文件地址 D:\\upload\\解压\\a.zip
* @param tagert 解压目的地址 D:\\upload\\解压\\
* @throws IOException
*/
public String unZip(String zipfile, String tagert) throws IOException {
try {
long startTime = System.currentTimeMillis();
File zfile = new File(zipfile);
FileInputStream fis = new FileInputStream(zfile);
String code = "UTF-8";
try {
code = resolveCode(zipfile);
} catch (Exception e) {
e.printStackTrace();
}
//默认UTF-8
Charset charset = Charset.forName(code);
// 输入源zip路径
ZipInputStream zis = new ZipInputStream(fis, charset);
ZipEntry entry = null;
File file = new File(tagert);
if (!file.exists()) {
file.mkdir();
}
String s = null;
BufferedOutputStream bos = null;
while ((entry = zis.getNextEntry()) != null) {
/** 去除文件名的空格和括号 */
String name = entry.getName().replaceAll("\\s*", "").replaceAll("\\(", "").replaceAll("\\)", "");
if (entry.isDirectory()) {
File filePath = new File(tagert + name);
//如果目录不存在,则创建
if (!filePath.exists()) {
filePath.mkdirs();
}
} else {
//当压缩包中包含目录时,程序会当成文件执行,所以添加下面代码进行控制
if (String.valueOf(entry).contains("/")) {
String s1 = name.substring(0, name.lastIndexOf("/"));
String s2 = name.substring(name.lastIndexOf("/") + 1);
File filePath = new File(tagert + s1 + File.separator);
if (!filePath.exists()) {
filePath.mkdir();
}
s = s1 + File.separator;
name = s1 + File.separator + s2;
}
System.err.println(tagert + name.trim());
FileOutputStream fos = new FileOutputStream(tagert + name.trim());
bos = new BufferedOutputStream(fos);
byte buf[] = new byte[1024];
int len;
while ((len = zis.read(buf)) != -1) {
bos.write(buf, 0, len);
}
zis.closeEntry();
//关闭的时候会刷新
bos.close();
}
}
//当压缩包存在下级目录时
if (StrUtil.isNotBlank(s)) {
tagert += s;
}
zis.close();
long endTime = System.currentTimeMillis();
System.out.println("解压完成!所需时间为:" + (endTime - startTime) + "ms");
} catch (IOException e) {
e.printStackTrace();
return "";
}
return tagert;
}
获取解压后的压缩包所有文件信息方法:
/**
* 拿到解压出来的图片信息
*
* @param zipfile
* @param target
* @return
* @throws IOException
*/
public List<String> unZipAndGetImagePaths(String zipfile, String target) throws IOException {
String resultPath = ZipUtil.unZip(zipfile, target); // 先调用原有解压方法
if (resultPath.isEmpty()) {
System.err.println("解压失败,无法获取图片数据");
return new ArrayList<>(); // 返回一个空列表
}
java.io.File targetFolder = new java.io.File(resultPath);
// 遍历解压目录,查找图片文件,并收集图片路径
List<String> imagePaths = new ArrayList<>();
if (targetFolder.exists() && targetFolder.isDirectory()) {
java.io.File[] files = targetFolder.listFiles((dir, name) -> {
String lowercaseName = name.toLowerCase();
for (String extension : new String[]{"jpg", "jpeg", "png", "gif", "bmp"}) {
if (lowercaseName.endsWith("." + extension)) {
return true;
}
}
return false;
});
if (files != null) {
System.out.println("找到的图片文件如下:");
for (java.io.File imageFile : files) {
// 收集图片的绝对路径
String imagePath = imageFile.getAbsolutePath();
imagePaths.add(imagePath);
// 这里只是打印文件名和大小作为示例,你可以根据需要处理这些图片数据
System.out.println("图片名: " + imageFile.getName() + ", 大小: " + Files.size(imageFile.toPath()) + " bytes");
}
} else {
System.out.println("解压目录下没有找到图片文件");
}
} else {
System.err.println("目标路径不存在或不是一个目录");
}
return imagePaths;
}
生成对应导入图片的hash值方法:
/**
* 根据导入的图片,生成对应的图片hash值
*
* @param targetPathList
*/
private Map<String, String> getImportImgHashValue(List<String> targetPathList) {
// 存储结果的Map
Map<String, String> imageHashMap = new HashMap<>();
try {
for (String imagePath : targetPathList) {
// 从路径中提取图片名称
String imageName = new java.io.File(imagePath).getName();
try {
// 读取图片
BufferedImage image = ImageIO.read(new java.io.File(imagePath));
// 计算哈希值
ContrastImgUntil contrastImgUtil = new ContrastImgUntil(image);
String hashString = contrastImgUtil.getHashAsString(); // 假设这个方法已正确实现
// 将图片名与哈希值存入Map
imageHashMap.put(imageName, hashString);
} catch (IOException e) {
System.err.println("Error reading or processing image: " + imagePath);
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
// 打印结果Map
for (Map.Entry<String, String> entry : imageHashMap.entrySet()) {
System.out.println("Image Name: " + entry.getKey() + ", Hash Value: " + entry.getValue());
}
return imageHashMap;
}
设置DuplicateResult 返回对象类:
import lombok.Data;
import java.util.List;
@Data
public class DuplicateResult {
private int duplicatesCount;
private List<String> allDuplicates;
public DuplicateResult(int duplicatesCount, List<String> allDuplicates) {
this.duplicatesCount = duplicatesCount;
this.allDuplicates = allDuplicates;
}
}
hash比较图片是否相同的两种方法:
1.直接获取当前目录下的所有图片信息,该数据会存对应的hash值,第一种方式就是拿出来直接比较效率高点
2.较为笨重、效率低(不推荐),导入的图片和原数据库图片进行比较,两个都要先转换为hash值再比较
/**
* 根据哈希值比较图片是否相同
*
* @param baseImageHashes 原数据库图片的哈希值集合,键为图片名,值为哈希值
* @param imageHashes 待比较图片的哈希值集合,键为图片名,值为哈希值(最新解压导入文件图片数据、hash值集合)
*/
public DuplicateResult compareImagesWithBaseline(Map<String, String> baseImageHashes, Map<String, String> imageHashes) {
if (baseImageHashes == null || imageHashes == null) {
throw new IllegalArgumentException("图片哈希值集合不能为空");
}
int duplicateCount = 0;
List<String> duplicates = new ArrayList<>();
// 遍历待比较图片的哈希值集合
for (Map.Entry<String, String> entry : imageHashes.entrySet()) {
String imageName = entry.getKey();
String imageHash = entry.getValue();
// 在原数据库图片哈希值集合中查找是否有相同的哈希值
if (baseImageHashes.containsValue(imageHash)) {
duplicates.add(imageName);
duplicateCount++;
}
}
return new DuplicateResult(duplicateCount, duplicates);
}
/**
* 根据hash比较图片是否相同
*
* @param baseImagePaths 原数据库图片数据
* @param imagePaths 导人解压出来的图片数据
*/
// public DuplicateResult compareImagesWithBaseline(List<String> baseImagePaths, List<String> imagePaths) {
// if (baseImagePaths == null || imagePaths == null) {
// throw new IllegalArgumentException("图片路径集合不能为空");
// }
// Map<String, List<String>> duplicatesMap = new HashMap<>(); // 使用Map存储重复图片路径
// int duplicateCount = 0; // 引入计数器变量
//
// // 对于每一张导入的图片,与原图集合中的所有图片进行比较
// for (String baseImagePath : baseImagePaths) {
// try {
// BufferedImage baseImage = ImageIO.read(new java.io.File(baseImagePath));
// ContrastImgUntil baseComparator = new ContrastImgUntil(baseImage);
//
// for (String imagePath : imagePaths) {
// try {
// BufferedImage imgToCompare = ImageIO.read(new java.io.File(imagePath));
// ContrastImgUntil comparator = new ContrastImgUntil(imgToCompare);
//
// float similarity = baseComparator.compare(comparator);
//
// // 处理相似度结果
// if (similarity == SIMILARITY_THRESHOLD) {
// // 注意这里交换了basePath和imagePath的位置以更准确地反映哪两张图片重复
// String errorMsg = String.format("导入图片 '%s' 与原图 '%s' 相似度为100%%,存在重复!", imagePath, baseImagePath);
// System.err.println(errorMsg + "分隔符!!!");
// duplicateCount++; // 每发现一次重复就增加计数
// duplicatesMap.computeIfAbsent(baseImagePath, k -> new ArrayList<>()).add(imagePath);
// }
// } catch (IOException e) {
// e.printStackTrace(); // 处理解码图片时可能发生的异常
// } catch (Exception ice) {
// System.err.println(ice.getMessage());
// }
// }
// } catch (IOException e) {
// e.printStackTrace(); // 处理解码图片时可能发生的异常
// } catch (Exception ice) {
// System.err.println(ice.getMessage());
// }
// }
// // 将所有重复图片路径合并到一个列表中
// List<String> allDuplicates = new ArrayList<>();
// for (List<String> group : duplicatesMap.values()) {
// allDuplicates.addAll(group);
// }
//
// // 返回重复图片的数量和重复图片
// return new DuplicateResult(duplicateCount, allDuplicates);
// }
以上内容繁多,请花点时间具体看写每个部分要点.
标签:hash,String,ZIP,java,return,File,new,压缩包,图片 From: https://blog.csdn.net/weixin_55746254/article/details/139393807