> constraintViolations = validator.validate(object, groups);
+ if (!constraintViolations.isEmpty())
+ {
+ throw new ConstraintViolationException(constraintViolations);
+ }
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileCompressUtil.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileCompressUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..450f6743e14ff62870498934f57edfedb9c99e8a
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileCompressUtil.java
@@ -0,0 +1,55 @@
+package com.RVSmartPorting.common.utils.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class FileCompressUtil {
+
+ public static String compressDirectory(String localRepositoryPath) {
+ File directoryToCompress = new File(localRepositoryPath);
+ String zipFileName = directoryToCompress.getName() + ".zip";
+ File zipFile = new File(directoryToCompress.getParent(), zipFileName);
+
+ try (FileOutputStream fos = new FileOutputStream(zipFile);
+ ZipOutputStream zos = new ZipOutputStream(fos)) {
+
+ compress(directoryToCompress, zos, "");
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return zipFile.getAbsolutePath();
+ }
+
+ private static void compress(File fileToCompress, ZipOutputStream zos, String parentDirectoryName) throws IOException {
+ if (fileToCompress.isDirectory()) {
+ String[] children = fileToCompress.list();
+ if (children != null) {
+ for (String child : children) {
+ compress(new File(fileToCompress, child), zos, parentDirectoryName + fileToCompress.getName() + "/");
+ }
+ }
+ } else {
+ byte[] buffer = new byte[1024];
+ try (FileInputStream fis = new FileInputStream(fileToCompress)) {
+ zos.putNextEntry(new ZipEntry(parentDirectoryName + fileToCompress.getName()));
+ int length;
+ while ((length = fis.read(buffer)) > 0) {
+ zos.write(buffer, 0, length);
+ }
+ zos.closeEntry();
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ String localRepositoryPath = "C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\test6\\11";
+ String compressedFilePath = compressDirectory(localRepositoryPath);
+ System.out.println("Compressed file saved at: " + compressedFilePath);
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileDecompressUtil.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileDecompressUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..d33b12f734121823678e29658411a32622a5c3a8
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileDecompressUtil.java
@@ -0,0 +1,599 @@
+package com.RVSmartPorting.common.utils.file;
+
+import com.RVSmartPorting.common.enums.FileType;
+import com.github.junrar.Archive;
+import com.github.junrar.exception.RarException;
+import com.github.junrar.rarfile.FileHeader;
+import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
+import org.apache.commons.compress.archivers.sevenz.SevenZFile;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarInputStream;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * @refer https://www.cnblogs.com/peashooter/p/9455498.html
+ * @create 2023-08-26 16:26
+ */
+public class FileDecompressUtil {
+ /**
+ * 获取文件真实类型
+ *
+ * @param file 要获取类型的文件。
+ * @return 文件类型枚举。
+ */
+ private static FileType getFileType(File file) {
+ FileInputStream inputStream = null;
+ try {
+ inputStream = new FileInputStream(file);
+ byte[] head = new byte[4];
+ if (-1 == inputStream.read(head)) {
+ return FileType.UNKNOWN;
+ }
+ int headHex = 0;
+ for (byte b : head) {
+ headHex <<= 8;
+ headHex |= b;
+ }
+ switch (headHex) {
+ case 0x504B0304:
+ return FileType.ZIP;
+ case 0x776f7264:
+ return FileType.TAR;
+ case -0x51:
+ return FileType._7Z;
+ case 0x425a6839:
+ return FileType.BZ2;
+ case -0x74f7f8:
+ return FileType.GZ;
+ case 0x52617221:
+ return FileType.RAR;
+ default:
+ return FileType.UNKNOWN;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return FileType.UNKNOWN;
+ }
+
+ public static void decompressFile(File file, String targetPath, boolean delete) {
+ final FileType fileType = getFileType(file);
+ switch (fileType) {
+ case ZIP:
+ decompressZip(file, targetPath, delete);
+ break;
+ case TAR:
+ decompressTar(file, targetPath, delete);
+ break;
+ case BZ2:
+ decompressBZ2(file, targetPath, delete);
+ break;
+ case TAR_BZ2:
+ decompressTarBz2(file, targetPath, delete);
+ break;
+ case TAR_GZ:
+ decompressTarGz(file, targetPath, delete);
+ break;
+ case _7Z:
+ decompress7Z(file, targetPath, delete);
+ break;
+ case RAR4:
+ decompressRAR4(file, targetPath, delete);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * 构建目录
+ *
+ * @param outputDir 输出目录
+ * @param subDir 子目录
+ */
+ private static void createDirectory(String outputDir, String subDir) {
+ File file = new File(outputDir);
+ if (!(subDir == null || subDir.trim().equals(""))) {//子目录不为空
+ file = new File(outputDir + '/' + subDir);
+ }
+ if (!file.exists()) {
+ if (!file.getParentFile().exists()) {
+ file.getParentFile().mkdirs();
+ }
+ file.mkdirs();
+ }
+ }
+
+ /**
+ * zip解压
+ *
+ * @param srcFile zip源文件
+ * @param destDirPath 解压后的目标文件夹
+ * @throws RuntimeException 解压失败会抛出运行时异常
+ */
+ public static void decompressZip(File srcFile, String destDirPath, boolean delete) throws RuntimeException {
+ long start = System.currentTimeMillis();
+ // 判断源文件是否存在
+ if (!srcFile.exists()) {
+ throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
+ }
+ // 开始解压
+ ZipFile zipFile = null;
+ try {
+ zipFile = new ZipFile(srcFile);
+ Enumeration> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ // 如果是文件夹,就创建个文件夹
+ if (entry.isDirectory()) {
+ String dirPath = destDirPath + "/" + entry.getName();
+ File dir = new File(dirPath);
+ dir.mkdirs();
+ } else {
+ // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
+ File targetFile = new File(destDirPath + "/" + entry.getName());
+ // 保证这个文件的父文件夹必须要存在
+ if (!targetFile.getParentFile().exists()) {
+ targetFile.getParentFile().mkdirs();
+ }
+ targetFile.createNewFile();
+ // 将压缩文件内容写入到这个文件中
+ InputStream is = zipFile.getInputStream(entry);
+ FileOutputStream fos = new FileOutputStream(targetFile);
+ int len;
+ byte[] buf = new byte[2 * 1024];
+ while ((len = is.read(buf)) != -1) {
+ fos.write(buf, 0, len);
+ }
+ // 关流顺序,先打开的后关闭
+ fos.close();
+ is.close();
+ }
+ }
+ long end = System.currentTimeMillis();
+ } catch (Exception e) {
+ throw new RuntimeException("unzip error from ZipUtils", e);
+ } finally {
+ if (zipFile != null) {
+ try {
+ zipFile.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ srcFile.delete();
+ }
+ }
+ }
+
+ /**
+ * 解压缩tar文件
+ *
+ * @param file 压缩包文件
+ * @param targetPath 目标文件夹
+ * @param delete 解压后是否删除原压缩包文件
+ */
+ private static void decompressTar(File file, String targetPath, boolean delete) {
+ FileInputStream fis = null;
+ OutputStream fos = null;
+ TarInputStream tarInputStream = null;
+ try {
+ fis = new FileInputStream(file);
+ tarInputStream = new TarInputStream(fis, 1024 * 2);
+ // 创建输出目录
+ createDirectory(targetPath, null);
+
+ TarEntry entry = null;
+ while (true) {
+ entry = tarInputStream.getNextEntry();
+ if (entry == null) {
+ break;
+ }
+ if (entry.isDirectory()) {
+ createDirectory(targetPath, entry.getName()); // 创建子目录
+ } else {
+ fos = new FileOutputStream(new File(targetPath + File.separator + entry.getName()));
+ int count;
+ byte data[] = new byte[2048];
+ while ((count = tarInputStream.read(data)) != -1) {
+ fos.write(data, 0, count);
+ }
+ fos.flush();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (fis != null) {
+ fis.close();
+ }
+ if (fos != null) {
+ fos.close();
+ }
+ if (tarInputStream != null) {
+ tarInputStream.close();
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ file.delete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 解压缩bz2文件
+ *
+ * @param file 压缩包文件
+ * @param targetPath 目标文件夹
+ * @param delete 解压后是否删除原压缩包文件
+ */
+ public static void decompressBZ2(File file, String targetPath, boolean delete) {
+ FileInputStream fis = null;
+ OutputStream fos = null;
+ BZip2CompressorInputStream bis = null;
+ String suffix = ".bz2";
+ try {
+ fis = new FileInputStream(file);
+ bis = new BZip2CompressorInputStream(fis);
+ // 创建输出目录
+ createDirectory(targetPath, null);
+ File tempFile = new File(targetPath + File.separator + file.getName().replace(suffix, ""));
+ fos = new FileOutputStream(tempFile);
+
+ int count;
+ byte data[] = new byte[2048];
+ while ((count = bis.read(data)) != -1) {
+ fos.write(data, 0, count);
+ }
+ fos.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (fis != null) {
+ fis.close();
+ }
+ if (fos != null) {
+ fos.close();
+ }
+ if (bis != null) {
+ bis.close();
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ file.delete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 解压缩tar.bz2文件
+ *
+ * @param file 压缩包文件
+ * @param targetPath 目标文件夹
+ * @param delete 解压后是否删除原压缩包文件
+ */
+ public static void decompressTarBz2(File file, String targetPath, boolean delete) {
+ FileInputStream fis = null;
+ OutputStream fos = null;
+ BZip2CompressorInputStream bis = null;
+ TarInputStream tis = null;
+ try {
+ fis = new FileInputStream(file);
+ bis = new BZip2CompressorInputStream(fis);
+ tis = new TarInputStream(bis, 1024 * 2);
+ // 创建输出目录
+ createDirectory(targetPath, null);
+ TarEntry entry;
+ while ((entry = tis.getNextEntry()) != null) {
+ if (entry.isDirectory()) {
+ createDirectory(targetPath, entry.getName()); // 创建子目录
+ } else {
+ fos = new FileOutputStream(new File(targetPath + File.separator + entry.getName()));
+ int count;
+ byte data[] = new byte[2048];
+ while ((count = tis.read(data)) != -1) {
+ fos.write(data, 0, count);
+ }
+ fos.flush();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (fis != null) {
+ fis.close();
+ }
+ if (fos != null) {
+ fos.close();
+ }
+ if (bis != null) {
+ bis.close();
+ }
+ if (tis != null) {
+ tis.close();
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ file.delete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 解压缩tar.gz文件
+ *
+ * @param file 压缩包文件
+ * @param targetPath 目标文件夹
+ * @param delete 解压后是否删除原压缩包文件
+ */
+ private static void decompressTarGz(File file, String targetPath, boolean delete) {
+ FileInputStream fileInputStream = null;
+ BufferedInputStream bufferedInputStream = null;
+ GZIPInputStream gzipIn = null;
+ TarInputStream tarIn = null;
+ OutputStream out = null;
+ try {
+ fileInputStream = new FileInputStream(file);
+ bufferedInputStream = new BufferedInputStream(fileInputStream);
+ gzipIn = new GZIPInputStream(bufferedInputStream);
+ tarIn = new TarInputStream(gzipIn, 1024 * 2);
+
+ // 创建输出目录
+ createDirectory(targetPath, null);
+
+ TarEntry entry = null;
+ while ((entry = tarIn.getNextEntry()) != null) {
+ if (entry.isDirectory()) { // 是目录
+ createDirectory(targetPath, entry.getName()); // 创建子目录
+ } else { // 是文件
+ File tempFIle = new File(targetPath + File.separator + entry.getName());
+ createDirectory(tempFIle.getParent() + File.separator, null);
+ out = new FileOutputStream(tempFIle);
+ int len = 0;
+ byte[] b = new byte[2048];
+
+ while ((len = tarIn.read(b)) != -1) {
+ out.write(b, 0, len);
+ }
+ out.flush();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ if (tarIn != null) {
+ tarIn.close();
+ }
+ if (gzipIn != null) {
+ gzipIn.close();
+ }
+ if (bufferedInputStream != null) {
+ bufferedInputStream.close();
+ }
+ if (fileInputStream != null) {
+ fileInputStream.close();
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ file.delete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 解压缩gz文件
+ *
+ * @param file 压缩包文件
+ * @param targetPath 目标文件夹
+ * @param delete 解压后是否删除原压缩包文件
+ */
+ private static void decompressGz(File file, String targetPath, boolean delete) {
+ FileInputStream fileInputStream = null;
+ GZIPInputStream gzipIn = null;
+ OutputStream out = null;
+ String suffix = ".gz";
+ try {
+ fileInputStream = new FileInputStream(file);
+ gzipIn = new GZIPInputStream(fileInputStream);
+ // 创建输出目录
+ createDirectory(targetPath, null);
+
+ File tempFile = new File(targetPath + File.separator + file.getName().replace(suffix, ""));
+ out = new FileOutputStream(tempFile);
+ int count;
+ byte data[] = new byte[2048];
+ while ((count = gzipIn.read(data)) != -1) {
+ out.write(data, 0, count);
+ }
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ if (gzipIn != null) {
+ gzipIn.close();
+ }
+ if (fileInputStream != null) {
+ fileInputStream.close();
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ file.delete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 解压缩7z文件
+ *
+ * @param file 压缩包文件
+ * @param targetPath 目标文件夹
+ * @param delete 解压后是否删除原压缩包文件
+ */
+ private static void decompress7Z(File file, String targetPath, boolean delete) {
+ SevenZFile sevenZFile = null;
+ OutputStream outputStream = null;
+ try {
+ sevenZFile = new SevenZFile(file);
+ // 创建输出目录
+ createDirectory(targetPath, null);
+ SevenZArchiveEntry entry;
+
+ while ((entry = sevenZFile.getNextEntry()) != null) {
+ if (entry.isDirectory()) {
+ createDirectory(targetPath, entry.getName()); // 创建子目录
+ } else {
+ outputStream = new FileOutputStream(new File(targetPath + File.separator + entry.getName()));
+ int len = 0;
+ byte[] b = new byte[2048];
+ while ((len = sevenZFile.read(b)) != -1) {
+ outputStream.write(b, 0, len);
+ }
+ outputStream.flush();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (sevenZFile != null) {
+ sevenZFile.close();
+ }
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ file.delete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 解压缩RAR4文件
+ * 注:目前不支持直接解压缩RAR5文件,原因见:https://blog.csdn.net/weixin_39821604/article/details/111484465
+ *
+ * @param file 压缩包文件
+ * @param targetPath 目标文件夹
+ * @param delete 压缩后是否删除原压缩包文件
+ */
+ private static void decompressRAR4(File file, String targetPath, boolean delete) {
+ Archive archive = null;
+ OutputStream outputStream = null;
+ try {
+ archive = new Archive(file);
+ FileHeader fileHeader;
+
+ // 创建输出目录
+ createDirectory(targetPath, null);
+
+ while ((fileHeader = archive.nextFileHeader()) != null) {
+ if (fileHeader.isDirectory()) {
+ createDirectory(targetPath, fileHeader.getFileNameString().trim()); // 创建子目录
+ } else {
+ final File file1 = new File(targetPath + File.separator + fileHeader.getFileNameString().trim());
+ File parentDirectory = file1.getParentFile();
+ if (!parentDirectory.exists()) {
+ parentDirectory.mkdirs();
+ }
+ outputStream = new FileOutputStream(file1);
+ archive.extractFile(fileHeader, outputStream);
+ }
+ }
+
+ } catch (IOException | RarException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (archive != null) {
+ archive.close();
+ }
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ // 根据条件决定是否删除原压缩包文件
+ if (delete) {
+ file.delete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static void test_getFileType() {
+ File rarfile = new File("C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\test.rar");
+ final FileType rarfileType = FileDecompressUtil.getFileType(rarfile);
+ System.out.println("rarfileType.name() = " + rarfileType.name());
+ File zipfile = new File("C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\test.zip");
+ final FileType zipfileType = FileDecompressUtil.getFileType(zipfile);
+ System.out.println("zipfileType.name() = " + zipfileType.name());
+ }
+
+ public static void test_decompressRAR4() {
+ String testFilePath = "C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\testrar4\\1.rar";
+ String targetPath = "C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\testrar4";
+ boolean deleteOriginal = true; // Set this according to your test case
+ File testFile = new File(testFilePath);
+ FileDecompressUtil.decompressRAR4(testFile, targetPath, deleteOriginal);
+ }
+
+ public static void test_decompressZip() {
+ String testFilePath = "C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\testzip\\a.zip";
+ String targetPath = "C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\testzip";
+ boolean deleteOriginal = true; // Set this according to your test case
+ File testFile = new File(testFilePath);
+ FileDecompressUtil.decompressZip(testFile, targetPath, deleteOriginal);
+ }
+
+ public static void main(String[] args) {
+// test_getFileType();
+// test_decompressRAR4();
+// test_decompressZip();
+
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileHashUtil.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileHashUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fe0348fe6dcaae98cf2a951108416a0d57a4773
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileHashUtil.java
@@ -0,0 +1,60 @@
+package com.RVSmartPorting.common.utils.file;
+import java.security.MessageDigest;
+import org.bouncycastle.crypto.digests.SM3Digest;
+
+import java.io.*;
+
+
+public class FileHashUtil {
+ public enum HashAlgorithm {
+ SHA1,
+ SM3
+ }
+ public static String calculateHash(File file, HashAlgorithm algorithm) throws Exception {
+ FileInputStream fis = new FileInputStream(file);
+ byte[] buffer = new byte[8192];
+ int bytesRead;
+
+ if (algorithm == HashAlgorithm.SHA1) {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ while ((bytesRead = fis.read(buffer)) != -1) {
+ md.update(buffer, 0, bytesRead);
+ }
+ fis.close();
+ byte[] hash = md.digest();
+ StringBuilder hashHex = new StringBuilder();
+ for (byte b : hash) {
+ hashHex.append(String.format("%02x", b));
+ }
+ return hashHex.toString();
+ } else if (algorithm == HashAlgorithm.SM3) {
+ SM3Digest sm3 = new SM3Digest();
+ while ((bytesRead = fis.read(buffer)) != -1) {
+ sm3.update(buffer, 0, bytesRead);
+ }
+ fis.close();
+ byte[] hash = new byte[sm3.getDigestSize()];
+ sm3.doFinal(hash, 0);
+ StringBuilder hashHex = new StringBuilder();
+ for (byte b : hash) {
+ hashHex.append(String.format("%02x", b));
+ }
+ return hashHex.toString();
+ } else {
+ throw new IllegalArgumentException("Unsupported hash algorithm");
+ }
+ }
+
+ public static void main(String[] args) {
+ File file = new File("C:\\Users\\Lenovo\\Desktop\\ospp\\测试\\a.zip");
+ try {
+ String sha1Hash = FileHashUtil.calculateHash(file, HashAlgorithm.SHA1);
+ String sm3Hash = FileHashUtil.calculateHash(file, HashAlgorithm.SM3);
+
+ System.out.println("SHA-1 Hash: " + sha1Hash);
+ System.out.println("SM3 Hash: " + sm3Hash);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileTypeUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileTypeUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..64d27ce46a945e1218109e4425c9adb4c5fe1384
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileTypeUtils.java
@@ -0,0 +1,77 @@
+package com.RVSmartPorting.common.utils.file;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+
+/**
+ * 文件类型工具类
+ *
+ * @author ruoyi
+ */
+public class FileTypeUtils
+{
+ /**
+ * 获取文件类型
+ *
+ * 例如: ruoyi.txt, 返回: txt
+ *
+ * @param file 文件名
+ * @return 后缀(不含".")
+ */
+ public static String getFileType(File file)
+ {
+ if (null == file)
+ {
+ return StringUtils.EMPTY;
+ }
+ return getFileType(file.getName());
+ }
+
+ /**
+ * 获取文件类型
+ *
+ * 例如: ruoyi.txt, 返回: txt
+ *
+ * @param fileName 文件名
+ * @return 后缀(不含".")
+ */
+ public static String getFileType(String fileName)
+ {
+ int separatorIndex = fileName.lastIndexOf(".");
+ if (separatorIndex < 0)
+ {
+ return "";
+ }
+ return fileName.substring(separatorIndex + 1).toLowerCase();
+ }
+
+ /**
+ * 获取文件类型
+ *
+ * @param photoByte 文件字节码
+ * @return 后缀(不含".")
+ */
+ public static String getFileExtendName(byte[] photoByte)
+ {
+ String strFileExtendName = "JPG";
+ if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+ && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
+ {
+ strFileExtendName = "GIF";
+ }
+ else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
+ {
+ strFileExtendName = "JPG";
+ }
+ else if ((photoByte[0] == 66) && (photoByte[1] == 77))
+ {
+ strFileExtendName = "BMP";
+ }
+ else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
+ {
+ strFileExtendName = "PNG";
+ }
+ return strFileExtendName;
+ }
+}
\ No newline at end of file
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileUploadUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileUploadUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..7edcc4368f1f1440ca1d22ddee62c55bab356340
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileUploadUtils.java
@@ -0,0 +1,273 @@
+package com.RVSmartPorting.common.utils.file;
+
+import com.RVSmartPorting.common.config.CSPConfig;
+import com.RVSmartPorting.common.constant.Constants;
+import com.RVSmartPorting.common.core.domain.entity.SysUser;
+import com.RVSmartPorting.common.exception.file.FileNameLengthLimitExceededException;
+import com.RVSmartPorting.common.exception.file.FileSizeLimitExceededException;
+import com.RVSmartPorting.common.exception.file.InvalidExtensionException;
+import com.RVSmartPorting.common.utils.DateUtils;
+import com.RVSmartPorting.common.utils.StringUtils;
+import com.RVSmartPorting.common.utils.uuid.Seq;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+/**
+ * 文件上传工具类
+ *
+ * @author ruoyi
+ */
+@Slf4j
+public class FileUploadUtils {
+ /**
+ * 默认大小 100M
+ */
+ public static final long DEFAULT_MAX_SIZE = 100 * 1024 * 1024;
+
+ /**
+ * 默认的文件名最大长度 100
+ */
+ public static final int DEFAULT_FILE_NAME_LENGTH = 100;
+
+ /**
+ * 默认上传的地址
+ */
+ private static String defaultBaseDir = CSPConfig.getProfile();
+
+ public static void setDefaultBaseDir(String defaultBaseDir) {
+ FileUploadUtils.defaultBaseDir = defaultBaseDir;
+ }
+
+ public static String getDefaultBaseDir() {
+ return defaultBaseDir;
+ }
+
+ /**
+ * 以默认配置进行文件上传
+ *
+ * @param file 上传的文件
+ * @return 文件名称
+ * @throws Exception
+ */
+ public static final String upload(MultipartFile file) throws IOException {
+ try {
+ return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+ public static void saveResourceToFile(Resource resource, String localFileStoragePath) {
+ try {
+ // 创建文件对象
+ File localCodePackFile = new File(localFileStoragePath);
+ // 检查文件所在的目录是否存在,如果不存在则创建
+ File parentDirectory = localCodePackFile.getParentFile();
+ if (!parentDirectory.exists()) {
+ parentDirectory.mkdirs();
+ }
+ FileOutputStream outputStream = new FileOutputStream(localCodePackFile);
+ FileCopyUtils.copy(resource.getInputStream(), outputStream);
+ outputStream.close();
+ log.info("File downloaded and saved to: {}", localFileStoragePath);
+ } catch (IOException e) {
+ log.error("Error while saving file: " + e.getMessage());
+ }
+ }
+ /**
+ * 根据文件路径上传
+ *
+ * @param baseDir 相对应用的基目录
+ * @param file 上传的文件
+ * @return 文件名称
+ * @throws IOException
+ */
+ public static final String upload(String baseDir, MultipartFile file) throws IOException {
+ try {
+ return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+ } catch (Exception e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 文件上传
+ *
+ * @param baseDir 相对应用的基目录
+ * @param file 上传的文件
+ * @param allowedExtension 上传文件类型
+ * @return 返回上传成功的文件名
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws FileNameLengthLimitExceededException 文件名太长
+ * @throws IOException 比如读写文件出错时
+ * @throws InvalidExtensionException 文件校验异常
+ */
+ public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
+ throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+ InvalidExtensionException {
+ int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
+ if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
+ throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+ }
+
+ assertAllowed(file, allowedExtension);
+
+ String fileName = extractFilename(file);
+
+ String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
+ file.transferTo(Paths.get(absPath));
+ return getPathFileName(baseDir, fileName);
+ }
+
+ /**
+ * 代码项目文件上传
+ *
+ * @param baseDir 相对应用的基目录
+ * @param file 上传的文件
+ * @return 返回上传成功的文件名
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws FileNameLengthLimitExceededException 文件名太长
+ * @throws IOException 比如读写文件出错时
+ * @throws InvalidExtensionException 文件校验异常
+ */
+ public static final String uploadCodePack2TmpFolder(String baseDir, MultipartFile file,
+ String[] allowedExtension)
+ throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+ InvalidExtensionException {
+ int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
+ if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
+ throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+ }
+ assertAllowed(file, allowedExtension);
+
+ String fileAbsDiskPath =
+ getAbsoluteFile(baseDir, file.getOriginalFilename()).getAbsolutePath();
+ FileUtils.cleanDirectory(new File(baseDir));
+ file.transferTo(Paths.get(fileAbsDiskPath));
+ return fileAbsDiskPath;
+ }
+ /**
+ * 代码项目文件上传
+ *
+ * @param baseDir 相对应用的基目录
+ * @param file 上传的文件
+ * @return 返回上传成功的文件名
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws FileNameLengthLimitExceededException 文件名太长
+ * @throws IOException 比如读写文件出错时
+ * @throws InvalidExtensionException 文件校验异常
+ */
+ public static final String uploadCodePack2TmpFolder(String baseDir, File file)
+ throws FileSizeLimitExceededException, IOException{
+ int fileNameLength = Objects.requireNonNull(file.getName()).length();
+ if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
+ throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+ }
+ String fileAbsDiskPath =
+ getAbsoluteFile(baseDir, file.getName()).getAbsolutePath();
+ FileUtils.cleanDirectory(new File(baseDir));
+ FileUtils.copyFile(file, new File(fileAbsDiskPath));
+ return fileAbsDiskPath;
+ }
+ /**
+ * 编码文件名
+ */
+ public static final String extractFilename(MultipartFile file) {
+ return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
+ FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
+ }
+
+ public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
+ File desc = new File(uploadDir + '/' + fileName);
+
+ if (!desc.exists()) {
+ if (!desc.getParentFile().exists()) {
+ desc.getParentFile().mkdirs();
+ }
+ }
+ return desc;
+ }
+
+ public static final String getPathFileName(String uploadDir, String fileName) throws IOException {
+ int dirLastIndex = CSPConfig.getProfile().length() + 1;
+ String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
+ return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
+ }
+
+ /**
+ * 文件大小校验
+ *
+ * @param file 上传的文件
+ * @return
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws InvalidExtensionException
+ */
+ public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
+ throws FileSizeLimitExceededException, InvalidExtensionException {
+ long size = file.getSize();
+ if (size > DEFAULT_MAX_SIZE) {
+ throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
+ }
+
+ String fileName = file.getOriginalFilename();
+ String extension = getExtension(file);
+ if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
+ if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
+ throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
+ fileName);
+ } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
+ throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
+ fileName);
+ } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
+ throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
+ fileName);
+ } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
+ throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
+ fileName);
+ } else if (allowedExtension == MimeTypeUtils.COMPRESS_EXTENSION) {
+ throw new InvalidExtensionException.InvalidCompressExtensionException(allowedExtension, extension,
+ fileName);
+ } else {
+ throw new InvalidExtensionException(allowedExtension, extension, fileName);
+ }
+ }
+ }
+
+ /**
+ * 判断MIME类型是否是允许的MIME类型
+ *
+ * @param extension
+ * @param allowedExtension
+ * @return
+ */
+ public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
+ for (String str : allowedExtension) {
+ if (str.equalsIgnoreCase(extension)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 获取文件名的后缀
+ *
+ * @param file 表单文件
+ * @return 后缀名
+ */
+ public static final String getExtension(MultipartFile file) {
+ String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+ if (StringUtils.isEmpty(extension)) {
+ extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
+ }
+ return extension;
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf39aaed414ecc366aa356cc5c12f664c0a95bab
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/FileUtils.java
@@ -0,0 +1,337 @@
+package com.RVSmartPorting.common.utils.file;
+
+
+import com.RVSmartPorting.common.config.CSPConfig;
+import com.RVSmartPorting.common.utils.DateUtils;
+import com.RVSmartPorting.common.utils.StringUtils;
+import com.RVSmartPorting.common.utils.uuid.IdUtils;
+import com.alibaba.fastjson.util.IOUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 文件处理工具类
+ *
+ * @author ruoyi
+ */
+@Slf4j
+public class FileUtils {
+ public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
+
+ public static void copyFile(File srcFile, File destFile) throws IOException {
+ org.apache.commons.io.FileUtils.copyFile(srcFile, destFile);
+ }
+
+ public static String extractFileNameFromUrl(String urlString) {
+ try {
+ URL url = new URL(urlString);
+ return new File(url.getPath()).getName();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null; // 或者返回一个默认的文件名
+ }
+ }
+
+ public static String extractFileName(ResponseEntity response) {
+ if (response.getStatusCode().is2xxSuccessful()) {
+ HttpHeaders headers = response.getHeaders();
+ String contentDisposition = headers.getFirst(HttpHeaders.CONTENT_DISPOSITION);
+
+ if (contentDisposition != null && contentDisposition.contains("filename=")) {
+ int startIndex = contentDisposition.indexOf("filename=") + 9; // 10 is the length of "filename=\""
+ return contentDisposition.substring(startIndex);
+ } else {
+ log.error("Content-Disposition header not found or does not contain a filename.");
+ return null;
+ }
+ } else {
+ log.error("Failed to download file. HTTP status code: " + response.getStatusCode());
+ return null;
+ }
+ }
+ public static void createFileIfNotExists(String filePath) {
+ File file = new File(filePath);
+ // 获取文件所在的父文件夹路径
+ String parentFolderPath = file.getParent();
+ File parentFolder = new File(parentFolderPath);
+
+ // 检查父文件夹是否存在,如果不存在则创建
+ if (!parentFolder.exists()) {
+ if (parentFolder.mkdirs()) {
+ log.info("父文件夹已成功创建:{}", parentFolderPath);
+ } else {
+ log.error("父文件夹创建失败:{}", parentFolderPath);
+ return; // 停止创建文件,因为父文件夹未创建成功
+ }
+ }
+ // 检查文件是否存在
+ if (file.exists()) {
+ log.info("文件已存在。");
+ } else {
+ try {
+ // 创建文件
+ if (file.createNewFile()) {
+ log.info("文件已成功创建。");
+ } else {
+ log.error("文件创建失败。");
+ }
+ } catch (IOException e) {
+ log.error("创建文件时出现异常:" + e.getMessage(), e);
+ }
+ }
+ }
+ public static void deleteDirectory(File directory) {
+ if (directory.exists()) {
+ File[] files = directory.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ // 递归删除子文件夹
+ deleteDirectory(file);
+ } else {
+ // 删除文件
+ file.delete();
+ }
+ }
+ }
+ // 删除空文件夹
+ directory.delete();
+ }
+ }
+
+ /**
+ * 输出指定文件的byte数组
+ *
+ * @param filePath 文件路径
+ * @param os 输出流
+ * @return
+ */
+ public static void writeBytes(String filePath, OutputStream os) throws IOException {
+ FileInputStream fis = null;
+ try {
+ File file = new File(filePath);
+ if (!file.exists()) {
+ throw new FileNotFoundException(filePath);
+ }
+ fis = new FileInputStream(file);
+ byte[] b = new byte[1024];
+ int length;
+ while ((length = fis.read(b)) > 0) {
+ os.write(b, 0, length);
+ }
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ IOUtils.close(os);
+ IOUtils.close(fis);
+ }
+ }
+
+ /**
+ * 写数据到文件中
+ *
+ * @param data 数据
+ * @return 目标文件
+ * @throws IOException IO异常
+ */
+ public static String writeImportBytes(byte[] data) throws IOException {
+ return writeBytes(data, CSPConfig.getImportPath());
+ }
+
+ /**
+ * 写数据到文件中
+ *
+ * @param data 数据
+ * @param uploadDir 目标文件
+ * @return 目标文件
+ * @throws IOException IO异常
+ */
+ public static String writeBytes(byte[] data, String uploadDir) throws IOException {
+ FileOutputStream fos = null;
+ String pathName = "";
+ try {
+ String extension = getFileExtendName(data);
+ pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
+ File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
+ fos = new FileOutputStream(file);
+ fos.write(data);
+ } finally {
+ IOUtils.close(fos);
+ }
+ return FileUploadUtils.getPathFileName(uploadDir, pathName);
+ }
+
+
+ public static void cleanDirectory(File file) throws IOException {
+ org.apache.commons.io.FileUtils.cleanDirectory(file);
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param filePath 文件
+ * @return
+ */
+ public static boolean deleteFile(String filePath) {
+ boolean flag = false;
+ File file = new File(filePath);
+ // 路径为文件且不为空则进行删除
+ if (file.isFile() && file.exists()) {
+ flag = file.delete();
+ }
+ return flag;
+ }
+
+ /**
+ * 文件名称验证
+ *
+ * @param filename 文件名称
+ * @return true 正常 false 非法
+ */
+ public static boolean isValidFilename(String filename) {
+ return filename.matches(FILENAME_PATTERN);
+ }
+
+ /**
+ * 检查文件是否可下载
+ *
+ * @param resource 需要下载的文件
+ * @return true 正常 false 非法
+ */
+ public static boolean checkAllowDownload(String resource) {
+ // 禁止目录上跳级别
+ if (StringUtils.contains(resource, "..")) {
+ return false;
+ }
+
+ // 检查允许下载的文件规则
+ if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) {
+ return true;
+ }
+
+ // 不在允许下载的文件规则
+ return false;
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param request 请求对象
+ * @param fileName 文件名
+ * @return 编码后的文件名
+ */
+ public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
+ final String agent = request.getHeader("USER-AGENT");
+ String filename = fileName;
+ if (agent.contains("MSIE")) {
+ // IE浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ filename = filename.replace("+", " ");
+ } else if (agent.contains("Firefox")) {
+ // 火狐浏览器
+ filename = new String(fileName.getBytes(), "ISO8859-1");
+ } else if (agent.contains("Chrome")) {
+ // google浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ } else {
+ // 其它浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ }
+ return filename;
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param response 响应对象
+ * @param realFileName 真实文件名
+ */
+ public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
+ String percentEncodedFileName = percentEncode(realFileName);
+
+ StringBuilder contentDispositionValue = new StringBuilder();
+ contentDispositionValue.append("attachment; filename=")
+ .append(percentEncodedFileName)
+ .append(";")
+ .append("filename*=")
+ .append("utf-8''")
+ .append(percentEncodedFileName);
+
+ response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
+ response.setHeader("Content-disposition", contentDispositionValue.toString());
+ response.setHeader("download-filename", percentEncodedFileName);
+ }
+
+ /**
+ * 百分号编码工具方法
+ *
+ * @param s 需要百分号编码的字符串
+ * @return 百分号编码后的字符串
+ */
+ public static String percentEncode(String s) throws UnsupportedEncodingException {
+ String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+ return encode.replaceAll("\\+", "%20");
+ }
+
+ /**
+ * 获取图像后缀
+ *
+ * @param photoByte 图像数据
+ * @return 后缀名
+ */
+ public static String getFileExtendName(byte[] photoByte) {
+ String strFileExtendName = "jpg";
+ if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+ && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
+ strFileExtendName = "gif";
+ } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
+ strFileExtendName = "jpg";
+ } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
+ strFileExtendName = "bmp";
+ } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
+ strFileExtendName = "png";
+ }
+ return strFileExtendName;
+ }
+
+ /**
+ * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
+ *
+ * @param fileName 路径名称
+ * @return 没有文件路径的名称
+ */
+ public static String getName(String fileName) {
+ if (fileName == null) {
+ return null;
+ }
+ int lastUnixPos = fileName.lastIndexOf('/');
+ int lastWindowsPos = fileName.lastIndexOf('\\');
+ int index = Math.max(lastUnixPos, lastWindowsPos);
+ return fileName.substring(index + 1);
+ }
+
+ /**
+ * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
+ *
+ * @param fileName 路径名称
+ * @return 没有文件路径和后缀的名称
+ */
+ public static String getNameNotSuffix(String fileName) {
+ if (fileName == null) {
+ return null;
+ }
+ String baseName = FilenameUtils.getBaseName(fileName);
+ return baseName;
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/ImageUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/ImageUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a41c9b42606af4b90cd706f25565de987677811
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/ImageUtils.java
@@ -0,0 +1,99 @@
+package com.RVSmartPorting.common.utils.file;
+
+import com.RVSmartPorting.common.config.CSPConfig;
+import com.RVSmartPorting.common.constant.Constants;
+import com.RVSmartPorting.common.utils.StringUtils;
+import org.apache.poi.util.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+
+/**
+ * 图片处理工具类
+ *
+ * @author ruoyi
+ */
+public class ImageUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
+
+ public static byte[] getImage(String imagePath)
+ {
+ InputStream is = getFile(imagePath);
+ try
+ {
+ return IOUtils.toByteArray(is);
+ }
+ catch (Exception e)
+ {
+ log.error("图片加载异常 {}", e);
+ return null;
+ }
+ finally
+ {
+ IOUtils.closeQuietly(is);
+ }
+ }
+
+ public static InputStream getFile(String imagePath)
+ {
+ try
+ {
+ byte[] result = readFile(imagePath);
+ result = Arrays.copyOf(result, result.length);
+ return new ByteArrayInputStream(result);
+ }
+ catch (Exception e)
+ {
+ log.error("获取图片异常 {}", e);
+ }
+ return null;
+ }
+
+ /**
+ * 读取文件为字节数据
+ *
+ * @param url 地址
+ * @return 字节数据
+ */
+ public static byte[] readFile(String url)
+ {
+ InputStream in = null;
+ try
+ {
+ if (url.startsWith("http"))
+ {
+ // 网络地址
+ URL urlObj = new URL(url);
+ URLConnection urlConnection = urlObj.openConnection();
+ urlConnection.setConnectTimeout(30 * 1000);
+ urlConnection.setReadTimeout(60 * 1000);
+ urlConnection.setDoInput(true);
+ in = urlConnection.getInputStream();
+ }
+ else
+ {
+ // 本机地址
+ String localPath = CSPConfig.getProfile();
+ String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
+ in = new FileInputStream(downloadPath);
+ }
+ return IOUtils.toByteArray(in);
+ }
+ catch (Exception e)
+ {
+ log.error("获取文件路径异常 {}", e);
+ return null;
+ }
+ finally
+ {
+ IOUtils.closeQuietly(in);
+ }
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/MimeTypeUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/MimeTypeUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..beedca3a1f1362547b5e48435ed92a709da28037
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/MimeTypeUtils.java
@@ -0,0 +1,60 @@
+package com.RVSmartPorting.common.utils.file;
+
+/**
+ * 媒体类型工具类
+ *
+ * @author ruoyi
+ */
+public class MimeTypeUtils {
+ public static final String IMAGE_PNG = "image/png";
+
+ public static final String IMAGE_JPG = "image/jpg";
+
+ public static final String IMAGE_JPEG = "image/jpeg";
+
+ public static final String IMAGE_BMP = "image/bmp";
+
+ public static final String IMAGE_GIF = "image/gif";
+
+ public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
+
+ public static final String[] FLASH_EXTENSION = {"swf", "flv"};
+
+ public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
+ "asf", "rm", "rmvb"};
+
+ public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
+
+ public static final String[] COMPRESS_EXTENSION = {"rar", "zip", "gz", "bz2", "7z", "xz", "bz2", "tar", "tar.gz",
+ "tar", "xz", "tar.bz2"};
+
+
+ public static final String[] DEFAULT_ALLOWED_EXTENSION = {
+ // 图片
+ "bmp", "gif", "jpg", "jpeg", "png",
+ // word excel powerpoint
+ "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
+ // 压缩文件
+ "rar", "zip", "gz", "bz2",
+ // 视频格式
+ "mp4", "avi", "rmvb",
+ // pdf
+ "pdf"};
+
+ public static String getExtension(String prefix) {
+ switch (prefix) {
+ case IMAGE_PNG:
+ return "png";
+ case IMAGE_JPG:
+ return "jpg";
+ case IMAGE_JPEG:
+ return "jpeg";
+ case IMAGE_BMP:
+ return "bmp";
+ case IMAGE_GIF:
+ return "gif";
+ default:
+ return "";
+ }
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/PathConverterUtil.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/PathConverterUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..01229ab0435a1e77c0342e920d83167bb91f65c6
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/file/PathConverterUtil.java
@@ -0,0 +1,36 @@
+package com.RVSmartPorting.common.utils.file;
+
+public class PathConverterUtil {
+ public static String convertPath(String absolutePath) {
+ // 获取当前操作系统的名称
+ String os = System.getProperty("os.name").toLowerCase();
+
+ // 如果是Windows系统,将路径分隔符替换为Windows风格
+ if (os.contains("win")) {
+ return absolutePath.replace("/", "\\");
+ }
+
+ // 如果是Linux系统,将路径分隔符替换为Linux风格
+ if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
+ return absolutePath.replace("\\", "/");
+ }
+
+ // 如果不是Windows或Linux,则保持原样
+ return absolutePath;
+ }
+ public static void test() {
+ // 示例用法
+ String absolutePath = "C:/Temp/test_upload_repo/upload/codePackStorageRoot/b7/a0/b7a0f48c7642d0264d0b5617d7773a0fa5ae9c90/1695285270657.zip";
+ String convertedPath = convertPath(absolutePath);
+ System.out.println("Converted Path: " + convertedPath);
+ }
+ public static void test2() {
+ String absolutePath = "C:/Users/User/Documents/file.txt";
+ String convertedPath = convertPath(absolutePath);
+ System.out.println("Converted Path: " + convertedPath);
+ }
+ public static void main(String[] args) {
+ // 示例用法
+ test();
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/html/EscapeUtil.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/html/EscapeUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc4f35052d44791d4705ea1d8ea2bbbedb953113
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/html/EscapeUtil.java
@@ -0,0 +1,157 @@
+package com.RVSmartPorting.common.utils.html;
+
+
+import com.RVSmartPorting.common.utils.StringUtils;
+
+/**
+ * 转义和反转义工具类
+ *
+ * @author ruoyi
+ */
+public class EscapeUtil
+{
+ public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
+
+ private static final char[][] TEXT = new char[64][];
+
+ static
+ {
+ for (int i = 0; i < 64; i++)
+ {
+ TEXT[i] = new char[] { (char) i };
+ }
+
+ // special HTML characters
+ TEXT['\''] = "'".toCharArray(); // 单引号
+ TEXT['"'] = """.toCharArray(); // 单引号
+ TEXT['&'] = "&".toCharArray(); // &符
+ TEXT['<'] = "<".toCharArray(); // 小于号
+ TEXT['>'] = ">".toCharArray(); // 大于号
+ }
+
+ /**
+ * 转义文本中的HTML字符为安全的字符
+ *
+ * @param text 被转义的文本
+ * @return 转义后的文本
+ */
+ public static String escape(String text)
+ {
+ return encode(text);
+ }
+
+ /**
+ * 还原被转义的HTML特殊字符
+ *
+ * @param content 包含转义符的HTML内容
+ * @return 转换后的字符串
+ */
+ public static String unescape(String content)
+ {
+ return decode(content);
+ }
+
+ /**
+ * 清除所有HTML标签,但是不删除标签内的内容
+ *
+ * @param content 文本
+ * @return 清除标签后的文本
+ */
+ public static String clean(String content)
+ {
+ return new HTMLFilter().filter(content);
+ }
+
+ /**
+ * Escape编码
+ *
+ * @param text 被编码的文本
+ * @return 编码后的字符
+ */
+ private static String encode(String text)
+ {
+ int len;
+ if ((text == null) || ((len = text.length()) == 0))
+ {
+ return StringUtils.EMPTY;
+ }
+ StringBuilder buffer = new StringBuilder(len + (len >> 2));
+ char c;
+ for (int i = 0; i < len; i++)
+ {
+ c = text.charAt(i);
+ if (c < 64)
+ {
+ buffer.append(TEXT[c]);
+ }
+ else
+ {
+ buffer.append(c);
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Escape解码
+ *
+ * @param content 被转义的内容
+ * @return 解码后的字符串
+ */
+ public static String decode(String content)
+ {
+ if (StringUtils.isEmpty(content))
+ {
+ return content;
+ }
+
+ StringBuilder tmp = new StringBuilder(content.length());
+ int lastPos = 0, pos = 0;
+ char ch;
+ while (lastPos < content.length())
+ {
+ pos = content.indexOf("%", lastPos);
+ if (pos == lastPos)
+ {
+ if (content.charAt(pos + 1) == 'u')
+ {
+ ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
+ tmp.append(ch);
+ lastPos = pos + 6;
+ }
+ else
+ {
+ ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
+ tmp.append(ch);
+ lastPos = pos + 3;
+ }
+ }
+ else
+ {
+ if (pos == -1)
+ {
+ tmp.append(content.substring(lastPos));
+ lastPos = content.length();
+ }
+ else
+ {
+ tmp.append(content.substring(lastPos, pos));
+ lastPos = pos;
+ }
+ }
+ }
+ return tmp.toString();
+ }
+
+ public static void main(String[] args)
+ {
+ String html = "";
+ // String html = "ipt>alert(\"XSS\")ipt>";
+ // String html = "<123";
+ // String html = "123>";
+ System.out.println(EscapeUtil.clean(html));
+ System.out.println(EscapeUtil.escape(html));
+ System.out.println(EscapeUtil.unescape(html));
+ }
+}
+
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/html/HTMLFilter.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/html/HTMLFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..477de4d6c172cd3bb57f9a3cf244ea759ab94695
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/html/HTMLFilter.java
@@ -0,0 +1,567 @@
+package com.RVSmartPorting.common.utils.html;
+
+/**
+ * HTML过滤器,用于去除XSS漏洞隐患。
+ *
+ * @author ruoyi
+ */
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class HTMLFilter
+{
+ /**
+ * regex flag union representing /si modifiers in php
+ **/
+ private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
+ private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL);
+ private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
+ private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
+ private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
+ private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
+ private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
+ private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
+ private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
+ private static final Pattern P_ENTITY = Pattern.compile("(\\d+);?");
+ private static final Pattern P_ENTITY_UNICODE = Pattern.compile("([0-9a-f]+);?");
+ private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
+ private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
+ private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
+ private static final Pattern P_END_ARROW = Pattern.compile("^>");
+ private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
+ private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
+ private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
+ private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
+ private static final Pattern P_AMP = Pattern.compile("&");
+ private static final Pattern P_QUOTE = Pattern.compile("\"");
+ private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
+ private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
+ private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
+
+ // @xxx could grow large... maybe use sesat's ReferenceMap
+ private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
+ private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
+
+ /**
+ * set of allowed html elements, along with allowed attributes for each element
+ **/
+ private final Map> vAllowed;
+ /**
+ * counts of open tags for each (allowable) html element
+ **/
+ private final Map vTagCounts = new HashMap<>();
+
+ /**
+ * html elements which must always be self-closing (e.g. "")
+ **/
+ private final String[] vSelfClosingTags;
+ /**
+ * html elements which must always have separate opening and closing tags (e.g. "")
+ **/
+ private final String[] vNeedClosingTags;
+ /**
+ * set of disallowed html elements
+ **/
+ private final String[] vDisallowed;
+ /**
+ * attributes which should be checked for valid protocols
+ **/
+ private final String[] vProtocolAtts;
+ /**
+ * allowed protocols
+ **/
+ private final String[] vAllowedProtocols;
+ /**
+ * tags which should be removed if they contain no content (e.g. "" or "")
+ **/
+ private final String[] vRemoveBlanks;
+ /**
+ * entities allowed within html markup
+ **/
+ private final String[] vAllowedEntities;
+ /**
+ * flag determining whether comments are allowed in input String.
+ */
+ private final boolean stripComment;
+ private final boolean encodeQuotes;
+ /**
+ * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. ""
+ * becomes " text "). If set to false, unbalanced angle brackets will be html escaped.
+ */
+ private final boolean alwaysMakeTags;
+
+ /**
+ * Default constructor.
+ */
+ public HTMLFilter()
+ {
+ vAllowed = new HashMap<>();
+
+ final ArrayList a_atts = new ArrayList<>();
+ a_atts.add("href");
+ a_atts.add("target");
+ vAllowed.put("a", a_atts);
+
+ final ArrayList img_atts = new ArrayList<>();
+ img_atts.add("src");
+ img_atts.add("width");
+ img_atts.add("height");
+ img_atts.add("alt");
+ vAllowed.put("img", img_atts);
+
+ final ArrayList no_atts = new ArrayList<>();
+ vAllowed.put("b", no_atts);
+ vAllowed.put("strong", no_atts);
+ vAllowed.put("i", no_atts);
+ vAllowed.put("em", no_atts);
+
+ vSelfClosingTags = new String[] { "img" };
+ vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
+ vDisallowed = new String[] {};
+ vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
+ vProtocolAtts = new String[] { "src", "href" };
+ vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
+ vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
+ stripComment = true;
+ encodeQuotes = true;
+ alwaysMakeTags = false;
+ }
+
+ /**
+ * Map-parameter configurable constructor.
+ *
+ * @param conf map containing configuration. keys match field names.
+ */
+ @SuppressWarnings("unchecked")
+ public HTMLFilter(final Map conf)
+ {
+
+ assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
+ assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
+ assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
+ assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
+ assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
+ assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
+ assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
+ assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
+
+ vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed"));
+ vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
+ vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
+ vDisallowed = (String[]) conf.get("vDisallowed");
+ vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
+ vProtocolAtts = (String[]) conf.get("vProtocolAtts");
+ vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
+ vAllowedEntities = (String[]) conf.get("vAllowedEntities");
+ stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
+ encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
+ alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
+ }
+
+ private void reset()
+ {
+ vTagCounts.clear();
+ }
+
+ // ---------------------------------------------------------------
+ // my versions of some PHP library functions
+ public static String chr(final int decimal)
+ {
+ return String.valueOf((char) decimal);
+ }
+
+ public static String htmlSpecialChars(final String s)
+ {
+ String result = s;
+ result = regexReplace(P_AMP, "&", result);
+ result = regexReplace(P_QUOTE, """, result);
+ result = regexReplace(P_LEFT_ARROW, "<", result);
+ result = regexReplace(P_RIGHT_ARROW, ">", result);
+ return result;
+ }
+
+ // ---------------------------------------------------------------
+
+ /**
+ * given a user submitted input String, filter out any invalid or restricted html.
+ *
+ * @param input text (i.e. submitted by a user) than may contain html
+ * @return "clean" version of input, with only valid, whitelisted html elements allowed
+ */
+ public String filter(final String input)
+ {
+ reset();
+ String s = input;
+
+ s = escapeComments(s);
+
+ s = balanceHTML(s);
+
+ s = checkTags(s);
+
+ s = processRemoveBlanks(s);
+
+ // s = validateEntities(s);
+
+ return s;
+ }
+
+ public boolean isAlwaysMakeTags()
+ {
+ return alwaysMakeTags;
+ }
+
+ public boolean isStripComments()
+ {
+ return stripComment;
+ }
+
+ private String escapeComments(final String s)
+ {
+ final Matcher m = P_COMMENTS.matcher(s);
+ final StringBuffer buf = new StringBuffer();
+ if (m.find())
+ {
+ final String match = m.group(1); // (.*?)
+ m.appendReplacement(buf, Matcher.quoteReplacement(""));
+ }
+ m.appendTail(buf);
+
+ return buf.toString();
+ }
+
+ private String balanceHTML(String s)
+ {
+ if (alwaysMakeTags)
+ {
+ //
+ // try and form html
+ //
+ s = regexReplace(P_END_ARROW, "", s);
+ // 不追加结束标签
+ s = regexReplace(P_BODY_TO_END, "<$1>", s);
+ s = regexReplace(P_XML_CONTENT, "$1<$2", s);
+
+ }
+ else
+ {
+ //
+ // escape stray brackets
+ //
+ s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
+ s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
+
+ //
+ // the last regexp causes '<>' entities to appear
+ // (we need to do a lookahead assertion so that the last bracket can
+ // be used in the next pass of the regexp)
+ //
+ s = regexReplace(P_BOTH_ARROWS, "", s);
+ }
+
+ return s;
+ }
+
+ private String checkTags(String s)
+ {
+ Matcher m = P_TAGS.matcher(s);
+
+ final StringBuffer buf = new StringBuffer();
+ while (m.find())
+ {
+ String replaceStr = m.group(1);
+ replaceStr = processTag(replaceStr);
+ m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
+ }
+ m.appendTail(buf);
+
+ // these get tallied in processTag
+ // (remember to reset before subsequent calls to filter method)
+ final StringBuilder sBuilder = new StringBuilder(buf.toString());
+ for (String key : vTagCounts.keySet())
+ {
+ for (int ii = 0; ii < vTagCounts.get(key); ii++)
+ {
+ sBuilder.append("").append(key).append(">");
+ }
+ }
+ s = sBuilder.toString();
+
+ return s;
+ }
+
+ private String processRemoveBlanks(final String s)
+ {
+ String result = s;
+ for (String tag : vRemoveBlanks)
+ {
+ if (!P_REMOVE_PAIR_BLANKS.containsKey(tag))
+ {
+ P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>" + tag + ">"));
+ }
+ result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
+ if (!P_REMOVE_SELF_BLANKS.containsKey(tag))
+ {
+ P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
+ }
+ result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
+ }
+
+ return result;
+ }
+
+ private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
+ {
+ Matcher m = regex_pattern.matcher(s);
+ return m.replaceAll(replacement);
+ }
+
+ private String processTag(final String s)
+ {
+ // ending tags
+ Matcher m = P_END_TAG.matcher(s);
+ if (m.find())
+ {
+ final String name = m.group(1).toLowerCase();
+ if (allowed(name))
+ {
+ if (false == inArray(name, vSelfClosingTags))
+ {
+ if (vTagCounts.containsKey(name))
+ {
+ vTagCounts.put(name, vTagCounts.get(name) - 1);
+ return "" + name + ">";
+ }
+ }
+ }
+ }
+
+ // starting tags
+ m = P_START_TAG.matcher(s);
+ if (m.find())
+ {
+ final String name = m.group(1).toLowerCase();
+ final String body = m.group(2);
+ String ending = m.group(3);
+
+ // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
+ if (allowed(name))
+ {
+ final StringBuilder params = new StringBuilder();
+
+ final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
+ final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
+ final List paramNames = new ArrayList<>();
+ final List paramValues = new ArrayList<>();
+ while (m2.find())
+ {
+ paramNames.add(m2.group(1)); // ([a-z0-9]+)
+ paramValues.add(m2.group(3)); // (.*?)
+ }
+ while (m3.find())
+ {
+ paramNames.add(m3.group(1)); // ([a-z0-9]+)
+ paramValues.add(m3.group(3)); // ([^\"\\s']+)
+ }
+
+ String paramName, paramValue;
+ for (int ii = 0; ii < paramNames.size(); ii++)
+ {
+ paramName = paramNames.get(ii).toLowerCase();
+ paramValue = paramValues.get(ii);
+
+ // debug( "paramName='" + paramName + "'" );
+ // debug( "paramValue='" + paramValue + "'" );
+ // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
+
+ if (allowedAttribute(name, paramName))
+ {
+ if (inArray(paramName, vProtocolAtts))
+ {
+ paramValue = processParamProtocol(paramValue);
+ }
+ params.append(' ').append(paramName).append("=\"").append(paramValue).append("\"");
+ }
+ }
+
+ if (inArray(name, vSelfClosingTags))
+ {
+ ending = " /";
+ }
+
+ if (inArray(name, vNeedClosingTags))
+ {
+ ending = "";
+ }
+
+ if (ending == null || ending.length() < 1)
+ {
+ if (vTagCounts.containsKey(name))
+ {
+ vTagCounts.put(name, vTagCounts.get(name) + 1);
+ }
+ else
+ {
+ vTagCounts.put(name, 1);
+ }
+ }
+ else
+ {
+ ending = " /";
+ }
+ return "<" + name + params + ending + ">";
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+ // comments
+ m = P_COMMENT.matcher(s);
+ if (!stripComment && m.find())
+ {
+ return "<" + m.group() + ">";
+ }
+
+ return "";
+ }
+
+ private String processParamProtocol(String s)
+ {
+ s = decodeEntities(s);
+ final Matcher m = P_PROTOCOL.matcher(s);
+ if (m.find())
+ {
+ final String protocol = m.group(1);
+ if (!inArray(protocol, vAllowedProtocols))
+ {
+ // bad protocol, turn into local anchor link instead
+ s = "#" + s.substring(protocol.length() + 1);
+ if (s.startsWith("#//"))
+ {
+ s = "#" + s.substring(3);
+ }
+ }
+ }
+
+ return s;
+ }
+
+ private String decodeEntities(String s)
+ {
+ StringBuffer buf = new StringBuffer();
+
+ Matcher m = P_ENTITY.matcher(s);
+ while (m.find())
+ {
+ final String match = m.group(1);
+ final int decimal = Integer.decode(match).intValue();
+ m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+ }
+ m.appendTail(buf);
+ s = buf.toString();
+
+ buf = new StringBuffer();
+ m = P_ENTITY_UNICODE.matcher(s);
+ while (m.find())
+ {
+ final String match = m.group(1);
+ final int decimal = Integer.valueOf(match, 16).intValue();
+ m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+ }
+ m.appendTail(buf);
+ s = buf.toString();
+
+ buf = new StringBuffer();
+ m = P_ENCODE.matcher(s);
+ while (m.find())
+ {
+ final String match = m.group(1);
+ final int decimal = Integer.valueOf(match, 16).intValue();
+ m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+ }
+ m.appendTail(buf);
+ s = buf.toString();
+
+ s = validateEntities(s);
+ return s;
+ }
+
+ private String validateEntities(final String s)
+ {
+ StringBuffer buf = new StringBuffer();
+
+ // validate entities throughout the string
+ Matcher m = P_VALID_ENTITIES.matcher(s);
+ while (m.find())
+ {
+ final String one = m.group(1); // ([^&;]*)
+ final String two = m.group(2); // (?=(;|&|$))
+ m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
+ }
+ m.appendTail(buf);
+
+ return encodeQuotes(buf.toString());
+ }
+
+ private String encodeQuotes(final String s)
+ {
+ if (encodeQuotes)
+ {
+ StringBuffer buf = new StringBuffer();
+ Matcher m = P_VALID_QUOTES.matcher(s);
+ while (m.find())
+ {
+ final String one = m.group(1); // (>|^)
+ final String two = m.group(2); // ([^<]+?)
+ final String three = m.group(3); // (<|$)
+ // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two)
+ m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
+ }
+ m.appendTail(buf);
+ return buf.toString();
+ }
+ else
+ {
+ return s;
+ }
+ }
+
+ private String checkEntity(final String preamble, final String term)
+ {
+
+ return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble;
+ }
+
+ private boolean isValidEntity(final String entity)
+ {
+ return inArray(entity, vAllowedEntities);
+ }
+
+ private static boolean inArray(final String s, final String[] array)
+ {
+ for (String item : array)
+ {
+ if (item != null && item.equals(s))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean allowed(final String name)
+ {
+ return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
+ }
+
+ private boolean allowedAttribute(final String name, final String paramName)
+ {
+ return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/FileDownloadListener.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/FileDownloadListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..5198637307146a66b1f0f2f62d257b227c855cd0
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/FileDownloadListener.java
@@ -0,0 +1,41 @@
+package com.RVSmartPorting.common.utils.http;
+
+import java.io.File;
+
+public interface FileDownloadListener {
+
+ /**
+ * 开始回调
+ *
+ */
+ void onDownloadStart();
+
+ /**
+ * 完成回调
+ *
+ * @param file 下载的文件
+ */
+ void onDownloadSuccess(File file);
+
+ /**
+ * 下载进度
+ *
+ * @param progress 下载进度
+ */
+ void onDownloading(int progress);
+
+ /**
+ * 出错回调
+ *
+ * @param e 异常
+ */
+ void onDownloadFailed(Exception e);
+
+ /**
+ * 超时回调
+ *
+ * @param e 异常
+ */
+ void onDownloadTimeout(Exception e);
+
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/HttpHelper.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/HttpHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a20a5638d9341e5d11160f6a3b8832bae22acff4
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/HttpHelper.java
@@ -0,0 +1,56 @@
+package com.RVSmartPorting.common.utils.http;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 通用http工具封装
+ *
+ * @author ruoyi
+ */
+public class HttpHelper
+{
+ private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
+
+ public static String getBodyString(ServletRequest request)
+ {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader reader = null;
+ try (InputStream inputStream = request.getInputStream())
+ {
+ reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ String line = "";
+ while ((line = reader.readLine()) != null)
+ {
+ sb.append(line);
+ }
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("getBodyString出现问题!");
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ LOGGER.error(ExceptionUtils.getMessage(e));
+ }
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/HttpUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/HttpUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fb590405e4fc43014ce8abb0de39657dd6506c4
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/http/HttpUtils.java
@@ -0,0 +1,260 @@
+package com.RVSmartPorting.common.utils.http;
+
+/**
+ * 通用http发送方法
+ *
+ * @author ruoyi
+ */
+
+import com.RVSmartPorting.common.constant.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.cert.X509Certificate;
+
+/**
+ * 通用http发送方法
+ *
+ * @author ruoyi
+ */
+public class HttpUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
+
+ /**
+ * 向指定 URL 发送GET方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendGet(String url, String param)
+ {
+ return sendGet(url, param, Constants.UTF8);
+ }
+
+ /**
+ * 向指定 URL 发送GET方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @param contentType 编码类型
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendGet(String url, String param, String contentType)
+ {
+ StringBuilder result = new StringBuilder();
+ BufferedReader in = null;
+ try
+ {
+ String urlNameString = url + "?" + param;
+ log.info("sendGet - {}", urlNameString);
+ URL realUrl = new URL(urlNameString);
+ URLConnection connection = realUrl.openConnection();
+ connection.setRequestProperty("accept", "*/*");
+ connection.setRequestProperty("connection", "Keep-Alive");
+ connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ connection.connect();
+ in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.append(line);
+ }
+ log.info("recv - {}", result);
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
+ }
+ finally
+ {
+ try
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 向指定 URL 发送POST方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendPost(String url, String param)
+ {
+ PrintWriter out = null;
+ BufferedReader in = null;
+ StringBuilder result = new StringBuilder();
+ try
+ {
+ String urlNameString = url;
+ log.info("sendPost - {}", urlNameString);
+ URL realUrl = new URL(urlNameString);
+ URLConnection conn = realUrl.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ conn.setRequestProperty("Accept-Charset", "utf-8");
+ conn.setRequestProperty("contentType", "utf-8");
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ out = new PrintWriter(conn.getOutputStream());
+ out.print(param);
+ out.flush();
+ in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.append(line);
+ }
+ log.info("recv - {}", result);
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
+ }
+ finally
+ {
+ try
+ {
+ if (out != null)
+ {
+ out.close();
+ }
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ catch (IOException ex)
+ {
+ log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+ }
+ }
+ return result.toString();
+ }
+
+ public static String sendSSLPost(String url, String param)
+ {
+ StringBuilder result = new StringBuilder();
+ String urlNameString = url + "?" + param;
+ try
+ {
+ log.info("sendSSLPost - {}", urlNameString);
+ SSLContext sc = SSLContext.getInstance("SSL");
+ sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
+ URL console = new URL(urlNameString);
+ HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ conn.setRequestProperty("Accept-Charset", "utf-8");
+ conn.setRequestProperty("contentType", "utf-8");
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+
+ conn.setSSLSocketFactory(sc.getSocketFactory());
+ conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
+ conn.connect();
+ InputStream is = conn.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String ret = "";
+ while ((ret = br.readLine()) != null)
+ {
+ if (ret != null && !"".equals(ret.trim()))
+ {
+ result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
+ }
+ }
+ log.info("recv - {}", result);
+ conn.disconnect();
+ br.close();
+ }
+ catch (ConnectException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
+ }
+ catch (SocketTimeoutException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+ }
+ catch (IOException e)
+ {
+ log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
+ }
+ catch (Exception e)
+ {
+ log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
+ }
+ return result.toString();
+ }
+
+ private static class TrustAnyTrustManager implements X509TrustManager
+ {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers()
+ {
+ return new X509Certificate[] {};
+ }
+ }
+
+ private static class TrustAnyHostnameVerifier implements HostnameVerifier
+ {
+ @Override
+ public boolean verify(String hostname, SSLSession session)
+ {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/ip/AddressUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/ip/AddressUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b418ad05bd0b3a6150b8f89de4dfaf2a06ec57e
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/ip/AddressUtils.java
@@ -0,0 +1,58 @@
+package com.RVSmartPorting.common.utils.ip;
+
+/**
+ * 获取地址类
+ *
+ * @author ruoyi
+ */
+
+import com.RVSmartPorting.common.constant.Constants;
+import com.RVSmartPorting.common.utils.StringUtils;
+import com.RVSmartPorting.common.utils.http.HttpUtils;
+import com.RVSmartPorting.common.config.CSPConfig;
+import com.alibaba.fastjson.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AddressUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
+
+ // IP地址查询
+ public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
+
+ // 未知地址
+ public static final String UNKNOWN = "XX XX";
+
+ public static String getRealAddressByIP(String ip)
+ {
+ String address = UNKNOWN;
+ // 内网不查询
+ if (IpUtils.internalIp(ip))
+ {
+ return "内网IP";
+ }
+ if (CSPConfig.isAddressEnabled())
+ {
+ try
+ {
+ String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
+ if (StringUtils.isEmpty(rspStr))
+ {
+ log.error("获取地理位置异常 {}", ip);
+ return UNKNOWN;
+ }
+ JSONObject obj = JSONObject.parseObject(rspStr);
+ String region = obj.getString("pro");
+ String city = obj.getString("city");
+ return String.format("%s %s", region, city);
+ }
+ catch (Exception e)
+ {
+ log.error("获取地理位置异常 {}", ip);
+ }
+ }
+ return address;
+ }
+}
+
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/ip/IpUtils.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/ip/IpUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..3540ff1828607026e2a60f5f70a23ff87790c39c
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/ip/IpUtils.java
@@ -0,0 +1,457 @@
+package com.RVSmartPorting.common.utils.ip;
+
+import com.RVSmartPorting.common.utils.ServletUtils;
+import com.RVSmartPorting.common.utils.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 获取IP方法
+ *
+ * @author ruoyi
+ */
+@Slf4j
+public class IpUtils {
+ public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
+ // 匹配 ip
+ public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
+ public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
+ // 匹配网段
+ public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
+
+ /**
+ * 获取客户端IP
+ *
+ * @return IP地址
+ */
+ public static String getIpAddr() {
+ return getIpAddr(ServletUtils.getRequest());
+ }
+
+ /**
+ * 获取客户端IP
+ *
+ * @param request 请求对象
+ * @return IP地址
+ */
+ public static String getIpAddr(HttpServletRequest request) {
+ if (request == null) {
+ return "unknown";
+ }
+ String ip = request.getHeader("x-forwarded-for");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("X-Forwarded-For");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("X-Real-IP");
+ }
+
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+
+ return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
+ }
+
+ /**
+ * 检查是否为内部IP地址
+ *
+ * @param ip IP地址
+ * @return 结果
+ */
+ public static boolean internalIp(String ip) {
+ byte[] addr = textToNumericFormatV4(ip);
+ return internalIp(addr) || "127.0.0.1".equals(ip);
+ }
+
+ /**
+ * 检查是否为内部IP地址
+ *
+ * @param addr byte地址
+ * @return 结果
+ */
+ private static boolean internalIp(byte[] addr) {
+ if (StringUtils.isNull(addr) || addr.length < 2) {
+ return true;
+ }
+ final byte b0 = addr[0];
+ final byte b1 = addr[1];
+ // 10.x.x.x/8
+ final byte SECTION_1 = 0x0A;
+ // 172.16.x.x/12
+ final byte SECTION_2 = (byte) 0xAC;
+ final byte SECTION_3 = (byte) 0x10;
+ final byte SECTION_4 = (byte) 0x1F;
+ // 192.168.x.x/16
+ final byte SECTION_5 = (byte) 0xC0;
+ final byte SECTION_6 = (byte) 0xA8;
+ switch (b0) {
+ case SECTION_1:
+ return true;
+ case SECTION_2:
+ if (b1 >= SECTION_3 && b1 <= SECTION_4) {
+ return true;
+ }
+ case SECTION_5:
+ switch (b1) {
+ case SECTION_6:
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * 将IPv4地址转换成字节
+ *
+ * @param text IPv4地址
+ * @return byte 字节
+ */
+ public static byte[] textToNumericFormatV4(String text) {
+ if (text.length() == 0) {
+ return null;
+ }
+
+ byte[] bytes = new byte[4];
+ String[] elements = text.split("\\.", -1);
+ try {
+ long l;
+ int i;
+ switch (elements.length) {
+ case 1:
+ l = Long.parseLong(elements[0]);
+ if ((l < 0L) || (l > 4294967295L)) {
+ return null;
+ }
+ bytes[0] = (byte) (int) (l >> 24 & 0xFF);
+ bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
+ bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+ bytes[3] = (byte) (int) (l & 0xFF);
+ break;
+ case 2:
+ l = Integer.parseInt(elements[0]);
+ if ((l < 0L) || (l > 255L)) {
+ return null;
+ }
+ bytes[0] = (byte) (int) (l & 0xFF);
+ l = Integer.parseInt(elements[1]);
+ if ((l < 0L) || (l > 16777215L)) {
+ return null;
+ }
+ bytes[1] = (byte) (int) (l >> 16 & 0xFF);
+ bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+ bytes[3] = (byte) (int) (l & 0xFF);
+ break;
+ case 3:
+ for (i = 0; i < 2; ++i) {
+ l = Integer.parseInt(elements[i]);
+ if ((l < 0L) || (l > 255L)) {
+ return null;
+ }
+ bytes[i] = (byte) (int) (l & 0xFF);
+ }
+ l = Integer.parseInt(elements[2]);
+ if ((l < 0L) || (l > 65535L)) {
+ return null;
+ }
+ bytes[2] = (byte) (int) (l >> 8 & 0xFF);
+ bytes[3] = (byte) (int) (l & 0xFF);
+ break;
+ case 4:
+ for (i = 0; i < 4; ++i) {
+ l = Integer.parseInt(elements[i]);
+ if ((l < 0L) || (l > 255L)) {
+ return null;
+ }
+ bytes[i] = (byte) (int) (l & 0xFF);
+ }
+ break;
+ default:
+ return null;
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ return bytes;
+ }
+
+ /**
+ * 获取IP地址
+ *
+ * @return 本地IP地址
+ */
+ public static String getHostIp() {
+ try {
+ return InetAddress.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ }
+ return "127.0.0.1";
+ }
+
+ /**
+ * 获取主机名
+ *
+ * @return 本地主机名
+ */
+ public static String getHostName() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ }
+ return "未知";
+ }
+
+ /**
+ * 从多级反向代理中获得第一个非unknown IP地址
+ *
+ * @param ip 获得的IP地址
+ * @return 第一个非unknown IP地址
+ */
+ public static String getMultistageReverseProxyIp(String ip) {
+ // 多级反向代理检测
+ if (ip != null && ip.indexOf(",") > 0) {
+ final String[] ips = ip.trim().split(",");
+ for (String subIp : ips) {
+ if (false == isUnknown(subIp)) {
+ ip = subIp;
+ break;
+ }
+ }
+ }
+ return StringUtils.substring(ip, 0, 255);
+ }
+
+ /**
+ * 检测给定字符串是否为未知,多用于检测HTTP请求相关
+ *
+ * @param checkString 被检测的字符串
+ * @return 是否未知
+ */
+ public static boolean isUnknown(String checkString) {
+ return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
+ }
+
+ /**
+ * 是否为IP
+ */
+ public static boolean isIP(String ip) {
+ return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
+ }
+
+ /**
+ * 是否为IP,或 *为间隔的通配符地址
+ */
+ public static boolean isIpWildCard(String ip) {
+ return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
+ }
+
+ /**
+ * 检测参数是否在ip通配符里
+ */
+ public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) {
+ String[] s1 = ipWildCard.split("\\.");
+ String[] s2 = ip.split("\\.");
+ boolean isMatchedSeg = true;
+ for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) {
+ if (!s1[i].equals(s2[i])) {
+ isMatchedSeg = false;
+ break;
+ }
+ }
+ return isMatchedSeg;
+ }
+
+ /**
+ * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
+ */
+ public static boolean isIPSegment(String ipSeg) {
+ return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
+ }
+
+ /**
+ * 判断ip是否在指定网段中
+ */
+ public static boolean ipIsInNetNoCheck(String iparea, String ip) {
+ int idx = iparea.indexOf('-');
+ String[] sips = iparea.substring(0, idx).split("\\.");
+ String[] sipe = iparea.substring(idx + 1).split("\\.");
+ String[] sipt = ip.split("\\.");
+ long ips = 0L, ipe = 0L, ipt = 0L;
+ for (int i = 0; i < 4; ++i) {
+ ips = ips << 8 | Integer.parseInt(sips[i]);
+ ipe = ipe << 8 | Integer.parseInt(sipe[i]);
+ ipt = ipt << 8 | Integer.parseInt(sipt[i]);
+ }
+ if (ips > ipe) {
+ long t = ips;
+ ips = ipe;
+ ipe = t;
+ }
+ return ips <= ipt && ipt <= ipe;
+ }
+
+ /**
+ * 校验ip是否符合过滤串规则
+ *
+ * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
+ * @param ip 校验IP地址
+ * @return boolean 结果
+ */
+ public static boolean isMatchedIp(String filter, String ip) {
+ if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) {
+ return false;
+ }
+ String[] ips = filter.split(";");
+ for (String iStr : ips) {
+ if (isIP(iStr) && iStr.equals(ip)) {
+ return true;
+ } else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) {
+ return true;
+ } else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isDomainMatch(String domainFilter, String domain) {
+ if (StringUtils.isBlank(domain)) {
+ return false;
+ }
+ // 去掉协议部分
+ String domainFilterWithoutProtocol = domainFilter.replaceFirst("^(https?://)?", "").replaceAll("/$", "");;
+ String domainWithoutProtocol = domain.replaceFirst("^(https?://)?", "");
+
+ // 使用正则表达式匹配域名
+ Pattern pattern = Pattern.compile(domainFilterWithoutProtocol, Pattern.CASE_INSENSITIVE);
+ Matcher matcher = pattern.matcher(domainWithoutProtocol);
+ return matcher.matches();
+ }
+
+ /**
+ * 校验ip或者域名是否符合过滤串规则
+ *
+ * @param filter 过滤列表,同时支持IP及域名,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`,支持域名通配符如:`.*google\.com;.*example\.org`
+ * @param URL 校验URL(Uniform Resource Locator)
+ * @return boolean 结果
+ */
+ public static boolean isMatchedIpAndDomain(String filter, String URL) {
+ if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(URL)) {
+ return false;
+ }
+ String domainOrIP = extractDomainOrIP(URL);
+ if (StringUtils.isEmpty(domainOrIP)) {
+ return false;
+ }
+ String[] ipsAndDomains = filter.split(";");
+ for (String iStrOrDomainStr : ipsAndDomains) {
+ if (isIP(iStrOrDomainStr) && iStrOrDomainStr.equals(domainOrIP)) {
+ return true;
+ } else if (isIpWildCard(iStrOrDomainStr) && ipIsInWildCardNoCheck(iStrOrDomainStr, domainOrIP)) {
+ return true;
+ } else if (isIPSegment(iStrOrDomainStr) && ipIsInNetNoCheck(iStrOrDomainStr, domainOrIP)) {
+ return true;
+ } else if (isDomainWildCard(iStrOrDomainStr) && isDomainMatch(iStrOrDomainStr, domainOrIP)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static String extractDomainOrIP(String path) {
+ try {
+ URI uri = new URI(path);
+ String host = uri.getHost();
+ if (host != null) {
+ // Removing "www." prefix if present
+ if (host.startsWith("www.")) {
+ host = host.substring(4);
+ }
+ return host;
+ }
+ } catch (URISyntaxException e) {
+ log.info("path extractDomain failed: " + path);
+ return "";
+ }
+ return "";
+ }
+
+ private static boolean isDomainWildCard(String iStrOrDomainStr) {
+ return StringUtils.isNotBlank(iStrOrDomainStr);
+ }
+
+ public static void test1() {
+ String filter = ".*google\\.com;.*example\\.org";
+ String domain = "www.google.com";
+
+ if (IpUtils.isMatchedIpAndDomain(filter, domain)) {
+ System.out.println("Domain matches filter.");
+ } else {
+ System.out.println("Domain does not match filter.");
+ }
+ }
+
+ public static void test2() {
+ String path1 = "http://www.google.com";
+ String path2 = "https://example.org/a/b.zip";
+ String path3 = "https://101.132.168.156/a/b.zip";
+
+ String domain1 = extractDomainOrIP(path1);
+ String domain2 = extractDomainOrIP(path2);
+ String domain3 = extractDomainOrIP(path3);
+ if (domain1 != null) {
+ System.out.println("Domain from path 1: " + domain1);
+ } else {
+ System.out.println("Failed to extract domain from path 1.");
+ }
+ if (domain2 != null) {
+ System.out.println("Domain from path 2: " + domain2);
+ } else {
+ System.out.println("Failed to extract domain from path 2.");
+ }
+ if (domain3 != null) {
+ System.out.println("Domain from path 3: " + domain3);
+ } else {
+ System.out.println("Failed to extract domain from path 3.");
+ }
+ }
+
+ public static void test3() {
+ String filter = ".*google\\.com;.*example\\.org;101.132.153.123";
+ String domain1 = "http://www.google.com";
+ System.out.println(IpUtils.isMatchedIpAndDomain(filter, domain1));
+ String domain2 = "http://www.google.com/a/b/zip";
+ System.out.println(IpUtils.isMatchedIpAndDomain(filter, domain2));
+ String domain3 = "http://101.132.153.123/a/b/zip";
+ System.out.println(IpUtils.isMatchedIpAndDomain(filter, domain3));
+ }
+
+ public static void test4() throws URISyntaxException {
+ String path="www.google.com";
+ URI uri = new URI(path);
+ String host = uri.getHost();
+ System.out.println(host);
+ }
+
+ public static void test5() {
+ System.out.println(isDomainMatch("https://gitee.com/","gitee.com"));
+ }
+
+ public static void main(String[] args) throws URISyntaxException {
+ test5();
+ }
+}
\ No newline at end of file
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/poi/ExcelHandlerAdapter.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/poi/ExcelHandlerAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6649919c3855f10b3c71a81b9077d6b68b91b2c0
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/poi/ExcelHandlerAdapter.java
@@ -0,0 +1,19 @@
+package com.RVSmartPorting.common.utils.poi;
+
+/**
+ * Excel数据格式处理适配器
+ *
+ * @author ruoyi
+ */
+public interface ExcelHandlerAdapter
+{
+ /**
+ * 格式化
+ *
+ * @param value 单元格数据值
+ * @param args excel注解args参数组
+ *
+ * @return 处理后的值
+ */
+ Object format(Object value, String[] args);
+}
diff --git a/CSP-common/src/main/java/com/RVSmartPorting/common/utils/poi/ExcelUtil.java b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/poi/ExcelUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..f56f9b82f0aed6f72b5ff19963558b8e75501d0a
--- /dev/null
+++ b/CSP-common/src/main/java/com/RVSmartPorting/common/utils/poi/ExcelUtil.java
@@ -0,0 +1,1702 @@
+package com.RVSmartPorting.common.utils.poi;
+
+import com.RVSmartPorting.common.annotation.Excel;
+import com.RVSmartPorting.common.annotation.Excel.ColumnType;
+import com.RVSmartPorting.common.annotation.Excel.Type;
+import com.RVSmartPorting.common.annotation.Excels;
+import com.RVSmartPorting.common.config.CSPConfig;
+import com.RVSmartPorting.common.core.domain.AjaxResult;
+import com.RVSmartPorting.common.exception.UtilException;
+import com.RVSmartPorting.common.utils.DateUtils;
+import com.RVSmartPorting.common.utils.DictUtils;
+import com.RVSmartPorting.common.utils.StringUtils;
+import com.RVSmartPorting.common.utils.file.FileTypeUtils;
+import com.RVSmartPorting.common.utils.file.FileUtils;
+import com.RVSmartPorting.common.utils.file.ImageUtils;
+import com.RVSmartPorting.common.utils.reflect.ReflectUtils;
+import com.RVSmartPorting.common.core.text.Convert;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.RegExUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.ooxml.POIXMLDocumentPart;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.*;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Excel相关处理
+ *
+ * @author ruoyi
+ */
+public class ExcelUtil
+{
+ private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
+
+ public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
+
+ public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
+
+ /**
+ * 用于dictType属性数据存储,避免重复查缓存
+ */
+ public Map sysDictMap = new HashMap();
+
+ /**
+ * Excel sheet最大行数,默认65536
+ */
+ public static final int sheetSize = 65536;
+
+ /**
+ * 工作表名称
+ */
+ private String sheetName;
+
+ /**
+ * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
+ */
+ private Type type;
+
+ /**
+ * 工作薄对象
+ */
+ private Workbook wb;
+
+ /**
+ * 工作表对象
+ */
+ private Sheet sheet;
+
+ /**
+ * 样式列表
+ */
+ private Map styles;
+
+ /**
+ * 导入导出数据列表
+ */
+ private List list;
+
+ /**
+ * 注解列表
+ */
+ private List