Base64字符串根据文件头识别文件后缀 | Java Base64字符串根据文件头识别文件后缀

目前支持文件后缀识别 (待完善):

文件头(magic number)文件扩展名(ext name)备注
89504E47.pngPNG
FFD8FF.jpgJPEG
47494638.gifGIF
25504446.pdfPDF
504B0304.zipZIP/DOCX/XLSX
D0CF11E0.docDOC(老版Office)
0x0201.xlsXLS(老版Office)
7ZBCAF27.7z7-Zip
52617221.rarRAR
494433.mp3MP3
000001BA.mpgMPEG视频
000001B3.mpgMPEG视频
66747970.mp4MP4
4F676753.oggOGG
424D.bmpBMP
1F8B08.gzGZIP
3C3F786D6C.xmlXML
68746D6C3E.htmlHTML
494E53455254494F.sqlSQL文件
3C21444F435459504520.htmlHTML

代码:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class FileExtensionDetector {
 // 文件头(Magic Numbers)与文件后缀的映射表
 private static final Map FILE_SIGNATURES = new HashMap();
 static {
 FILE_SIGNATURES.put("89504E47", ".png"); // PNG
 FILE_SIGNATURES.put("FFD8FF", ".jpg"); // JPEG
 FILE_SIGNATURES.put("47494638", ".gif"); // GIF
 FILE_SIGNATURES.put("25504446", ".pdf"); // PDF
 FILE_SIGNATURES.put("504B0304", "zip"); // ZIP / DOCX / XLSX
 FILE_SIGNATURES.put("D0CF11E0", ".doc"); // DOC (老版Office)
 FILE_SIGNATURES.put("0x0201", ".xls"); // XLS (老版Office)
 FILE_SIGNATURES.put("7ZBCAF27", ".7z"); // 7-Zip
 FILE_SIGNATURES.put("52617221", ".rar"); // RAR
 FILE_SIGNATURES.put("494433", ".mp3"); // MP3
 FILE_SIGNATURES.put("000001BA", ".mpg"); // MPEG 视频
 FILE_SIGNATURES.put("000001B3", ".mpg"); // MPEG 视频
 FILE_SIGNATURES.put("66747970", ".mp4"); // MP4
 FILE_SIGNATURES.put("4F676753", ".ogg"); // OGG
 FILE_SIGNATURES.put("424D", ".bmp"); // BMP
 FILE_SIGNATURES.put("1F8B08", ".gz"); // GZIP
 FILE_SIGNATURES.put("3C3F786D6C", ".xml"); // XML
 FILE_SIGNATURES.put("68746D6C3E", ".html"); // HTML
 FILE_SIGNATURES.put("494E53455254494F", ".sql"); // SQL 文件
 FILE_SIGNATURES.put("3C21444F435459504520", ".html"); // HTML
 }
 /**
 * 解析 Base64 字符串并推断文件后缀
 *
 * @param base64String Base64编码的文件字符串
 * @return 文件的后缀名
 */
 public static String getFileExtension(String base64String) {
 byte[] fileData = Base64.getDecoder().decode(base64String);
 String fileHeader = bytesToHex(fileData, 4);
 // 通过文件头判断文件类型
 for (Map.Entry entry : FILE_SIGNATURES.entrySet()) {
 if (fileHeader.startsWith(entry.getKey())) {
 String extension = entry.getValue();
 if ("zip".equals(extension)) {
 return checkZipContent(fileData); // ZIP文件需要进一步判断
 }
 return extension;
 }
 }
 return "未知类型";
 }
 /**
 * 检查 ZIP 文件的内部结构,以识别 OFD、DOCX、XLSX 或普通 ZIP
 *
 * @param data ZIP 文件的二进制数据
 * @return 文件后缀 (.ofd, .docx, .xlsx, .zip)
 */
 private static String checkZipContent(byte[] data) {
 try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(data))) {
 ZipEntry entry;
 boolean hasOFD = false, hasWord = false, hasExcel = false;
 // 遍历 ZIP 文件的所有条目
 while ((entry = zis.getNextEntry()) != null) {
 String name = entry.getName().toLowerCase();
 if (name.equals("ofd.xml")) return ".ofd"; // OFD 文件
 if (name.startsWith("word/")) hasWord = true; // DOCX 文件
 if (name.startsWith("xl/")) hasExcel = true; // XLSX 文件
 }
 if (hasWord) return ".docx";
 if (hasExcel) return ".xlsx";
 } catch (IOException e) {
 e.printStackTrace();
 }
 return ".zip"; // 如果未匹配到特定结构,默认为 ZIP 文件
 }
 /**
 * 将字节数组的前 length 字节转换为十六进制字符串
 *
 * @param bytes 字节数组
 * @param length 转换长度
 * @return 十六进制字符串
 */
 private static String bytesToHex(byte[] bytes, int length) {
 StringBuilder sb = new StringBuilder();
 for (int i = 0; i < Math.min(bytes.length, length); i++) {
 sb.append(String.format("%02X", bytes[i]));
 }
 return sb.toString();
 }
 public static void main(String[] args) {
 // 示例 Base64 字符串(替换为你要测试的 Base64)
 String base64String = "UEsFBgAAAAAAAAAAAAAAAAAAAAAAAA==..."; // 这里填入你的 Base64 字符串
 // 获取文件后缀并输出
 String extension = getFileExtension(base64String);
 System.out.println("文件后缀: " + extension);
 }
}

在 Java 代码中,对于 FILE_SIGNATURES 映射表的定义中,FILE_SIGNATURES.put("504B0304", "zip"); 使用 "zip" 而不是 ".zip" 是因为在代码逻辑中,我们通过检查文件内容来进一步判断 ZIP 文件的具体类型(例如 DOCX、XLSX 或 OFD)。以下是代码中的相关逻辑及其原因:

原因说明:

  1. 统一格式

    • 在一些场景下,文件名不需要带上点(.),而只需要后缀名的简化形式。因此,在 FILE_SIGNATURES 中使用 zip 而不是 .zip 可能是为了保持一致性。
  2. 判断文件类型

    • 由于 ZIP 文件格式可以容纳多种文件类型(如 DOCX、XLSX),在检测到文件头为 504B0304 时,我们调用 checkZipContent 方法进一步解析文件内容,以确定实际的文件类型。该方法返回的后缀名(如 .docx.xlsx)会在前端进行最终的呈现。

代码片段说明:

getFileExtension 方法中,当检测到文件头为 504B0304 时,它会调用 checkZipContent 方法:

if ("zip".equals(extension)) {
 return checkZipContent(fileData); // ZIP文件需要进一步判断
}

这样,checkZipContent 方法会处理 ZIP 文件并返回具体的后缀。

更新建议:

如果为了使代码的可读性更强且一致,您可以将 ZIP 文件的后缀更改为 .zip,但是请确保在后续逻辑中进行相应的调整。以下是可能的修改:

FILE_SIGNATURES.put("504B0304", ".zip"); // 使用 .zip

并在 checkZipContent 方法中相应地处理:

if (hasWord) return ".docx"; // 如果是 Word 文档
if (hasExcel) return ".xlsx"; // 如果是 Excel 文档

总结:

选择 .zipzip 实际上是风格问题,但重要的是在后续处理过程中保持一致性。根据您的需要进行调整可以增强代码的可读性和逻辑清晰度。

作者:湖城彭于晏原文地址:https://blog.csdn.net/weixin_44190087/article/details/143018799

%s 个评论

要回复文章请先登录注册