|
@@ -0,0 +1,228 @@
|
|
|
+package com.ruoyi.common.utils;
|
|
|
+
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+public class FileTypeUtils {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(FileTypeUtils.class);
|
|
|
+
|
|
|
+ /** 文件类型映射 */
|
|
|
+ private static final Map<String, String> FILE_TYPE_MAP = new HashMap<>();
|
|
|
+
|
|
|
+ static {
|
|
|
+ // 图片
|
|
|
+ FILE_TYPE_MAP.put("FFD8FFE0", "jpg");
|
|
|
+ FILE_TYPE_MAP.put("FFD8FFE1", "jpg");
|
|
|
+ FILE_TYPE_MAP.put("FFD8FFE8", "jpg");
|
|
|
+ FILE_TYPE_MAP.put("89504E47", "png");
|
|
|
+ FILE_TYPE_MAP.put("47494638", "gif");
|
|
|
+ FILE_TYPE_MAP.put("424D", "bmp");
|
|
|
+ // 文档
|
|
|
+ FILE_TYPE_MAP.put("504B0304", "docx"); // 也可能是xlsx、pptx、zip
|
|
|
+ FILE_TYPE_MAP.put("D0CF11E0", "doc"); // 也可能是xls、ppt
|
|
|
+ FILE_TYPE_MAP.put("25504446", "pdf");
|
|
|
+ // 压缩文件
|
|
|
+ FILE_TYPE_MAP.put("504B0304", "zip");
|
|
|
+ FILE_TYPE_MAP.put("526172", "rar");
|
|
|
+ FILE_TYPE_MAP.put("377ABCAF", "7z");
|
|
|
+ // 视频
|
|
|
+ FILE_TYPE_MAP.put("000001BA", "mpg");
|
|
|
+ FILE_TYPE_MAP.put("000001B3", "mpg");
|
|
|
+ FILE_TYPE_MAP.put("6674706D", "mp4");
|
|
|
+ FILE_TYPE_MAP.put("41564920", "avi");
|
|
|
+ // 音频
|
|
|
+ FILE_TYPE_MAP.put("49443303", "mp3");
|
|
|
+ FILE_TYPE_MAP.put("57415645", "wav");
|
|
|
+ // 其他
|
|
|
+ FILE_TYPE_MAP.put("3C3F786D", "xml");
|
|
|
+ FILE_TYPE_MAP.put("7B5C7274", "rtf");
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 文本类型 */
|
|
|
+ private static final String[] TEXT_TYPES = {
|
|
|
+ "txt", "md", "json", "xml", "html", "sql", "js", "css", "java", "py", "c", "cpp", "php",
|
|
|
+ "log", "properties", "yml", "yaml", "ini", "conf"
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文件类型
|
|
|
+ *
|
|
|
+ * @param file MultipartFile
|
|
|
+ * @return 文件类型
|
|
|
+ */
|
|
|
+ public static String getFileType(MultipartFile file) {
|
|
|
+ String fileType = getFileTypeByMagicNumber(file);
|
|
|
+ if (StringUtils.isNotEmpty(fileType)) {
|
|
|
+ return fileType;
|
|
|
+ }
|
|
|
+ return getExtension(file);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过魔数获取文件类型
|
|
|
+ */
|
|
|
+ private static String getFileTypeByMagicNumber(MultipartFile file) {
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ return StringUtils.EMPTY;
|
|
|
+ }
|
|
|
+
|
|
|
+ try (InputStream is = file.getInputStream()) {
|
|
|
+ byte[] bytes = new byte[28];
|
|
|
+ if (is.read(bytes) < 28) {
|
|
|
+ return StringUtils.EMPTY;
|
|
|
+ }
|
|
|
+
|
|
|
+ String magicNumberHex = bytesToHexString(bytes);
|
|
|
+
|
|
|
+ // 判断文件类型
|
|
|
+ for (Map.Entry<String, String> entry : FILE_TYPE_MAP.entrySet()) {
|
|
|
+ String magicNumber = entry.getKey();
|
|
|
+ if (magicNumberHex.startsWith(magicNumber)) {
|
|
|
+ String type = entry.getValue();
|
|
|
+ // 处理ZIP类型的特殊情况
|
|
|
+ if ("zip".equals(type)) {
|
|
|
+ type = checkZipType(file.getOriginalFilename(), type);
|
|
|
+ }
|
|
|
+ // 处理MS Office旧版本的特殊情况
|
|
|
+ else if ("doc".equals(type)) {
|
|
|
+ type = checkOfficeType(file.getOriginalFilename(), type);
|
|
|
+ }
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否为文本文件
|
|
|
+ if (isTextFile(bytes)) {
|
|
|
+ return "txt";
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("获取文件类型失败", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return StringUtils.EMPTY;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查ZIP类型文件的具体类型
|
|
|
+ */
|
|
|
+ private static String checkZipType(String fileName, String defaultType) {
|
|
|
+ if (StringUtils.isEmpty(fileName)) {
|
|
|
+ return defaultType;
|
|
|
+ }
|
|
|
+ String extension = getExtension(fileName).toLowerCase();
|
|
|
+ switch (extension) {
|
|
|
+ case "docx":
|
|
|
+ case "xlsx":
|
|
|
+ case "pptx":
|
|
|
+ return extension;
|
|
|
+ default:
|
|
|
+ return "zip";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查MS Office旧版本文件的具体类型
|
|
|
+ */
|
|
|
+ private static String checkOfficeType(String fileName, String defaultType) {
|
|
|
+ if (StringUtils.isEmpty(fileName)) {
|
|
|
+ return defaultType;
|
|
|
+ }
|
|
|
+ String extension = getExtension(fileName).toLowerCase();
|
|
|
+ switch (extension) {
|
|
|
+ case "xls":
|
|
|
+ case "ppt":
|
|
|
+ return extension;
|
|
|
+ default:
|
|
|
+ return "doc";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查是否为文本文件
|
|
|
+ */
|
|
|
+ private static boolean isTextFile(byte[] bytes) {
|
|
|
+ int nullCount = 0;
|
|
|
+ for (byte b : bytes) {
|
|
|
+ if (b == 0) {
|
|
|
+ nullCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 文本文件通常不会包含太多的空字节
|
|
|
+ return nullCount < bytes.length * 0.1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 字节数组转16进制字符串
|
|
|
+ */
|
|
|
+ private static String bytesToHexString(byte[] bytes) {
|
|
|
+ StringBuilder stringBuilder = new StringBuilder();
|
|
|
+ if (bytes == null || bytes.length <= 0) {
|
|
|
+ return StringUtils.EMPTY;
|
|
|
+ }
|
|
|
+ for (byte b : bytes) {
|
|
|
+ int v = b & 0xFF;
|
|
|
+ String hv = Integer.toHexString(v);
|
|
|
+ if (hv.length() < 2) {
|
|
|
+ stringBuilder.append(0);
|
|
|
+ }
|
|
|
+ stringBuilder.append(hv);
|
|
|
+ }
|
|
|
+ return stringBuilder.toString().toUpperCase();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文件扩展名
|
|
|
+ */
|
|
|
+ public static String getExtension(String fileName) {
|
|
|
+ if (StringUtils.isEmpty(fileName)) {
|
|
|
+ return StringUtils.EMPTY;
|
|
|
+ }
|
|
|
+ String extension = StringUtils.EMPTY;
|
|
|
+ int lastIndex = fileName.lastIndexOf(".");
|
|
|
+ if (lastIndex > 0) {
|
|
|
+ extension = fileName.substring(lastIndex + 1).toLowerCase();
|
|
|
+ }
|
|
|
+ return extension;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取文件扩展名
|
|
|
+ */
|
|
|
+ public static String getExtension(MultipartFile file) {
|
|
|
+ if (file == null) {
|
|
|
+ return StringUtils.EMPTY;
|
|
|
+ }
|
|
|
+ return getExtension(file.getOriginalFilename());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是否是文本文件
|
|
|
+ */
|
|
|
+ public static boolean isText(String fileName) {
|
|
|
+ String extension = getExtension(fileName);
|
|
|
+ return isInArray(extension, TEXT_TYPES);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否在指定数组中
|
|
|
+ */
|
|
|
+ private static boolean isInArray(String value, String[] array) {
|
|
|
+ if (StringUtils.isEmpty(value)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for (String item : array) {
|
|
|
+ if (item.equalsIgnoreCase(value)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|