缘由:如果有一个没有后缀的文件,如何判断其类型?

一、常用文件头标识

1、图片

  • JPEG/JPG(.jpg)

FF D8 FF

  • PNG(.png)

89 50 4E 47

  • GIF(.gif)

47 49 46 38

  • Bitmap(.bmp)

42 4D

2、音频/视频

  • WAV(.wav)

57 41 56 45

  • AVI(.avi)

41 56 49 20

  • MPEG(.mpg)

00 00 01 BA

  • MP4(.mp4)

00 00 00 18 66 74 79 70 33 67 70 35

  • M4A、M4V

00 00 00 20 66 74 79 70

  • AMR(.amr)

23 21 41 4D 52

AMR:Adaptibve Multi-Rate,是一种音频格式。主要用于移动设备的音频,压缩比比较大,相对于其他压缩格式质量比较差,多用于人声、通话。

3、XML/HTML

  • XML(.xml)

3C 3F 78 6D 6C

  • HTML(.html)

68 74 6D 6C 3E

4、压缩文件

  • ZIP(.zip)

50 4B 03 04

  • RAR(.rar)

52 61 72 21

5、其他

  • PDF(.pdf)

25 50 44 46 2D 31 2E

二、工具

1、WinHex

WinHex是一个16进制文件编辑软件,可以使用此软件查看任何文件的16进制形式。

2、Java

  • 文件头与文件后缀映射(部分)
public static Map<String, String> typeMap = new HashMap<String, String>();
static{
	typeMap.put("FFD8FF", ".jpg");
	typeMap.put("89504E47", ".png");
	typeMap.put("47494638", ".gif");
	typeMap.put("424D", ".bmp");
	typeMap.put("57415645", ".wav");
	typeMap.put("41564920", ".avi");
	typeMap.put("000001BA", ".mpg");
	typeMap.put("3C3F786D6C", ".xml");
	typeMap.put("68746D6C3E", ".html");
	typeMap.put("2321414D52", ".amr");
}
  • 根据文件头返回文件后缀
public static String getFileType(String header){
	header = header.toUpperCase();
	for(String key : typeMap.keySet()){
		if(header.startsWith(key)){
			return typeMap.get(key);
		}
	}
	return null;
}
  • 从文件中读取文件头(前面length个字节)
public static String getFileHeader(File file, int length){
	InputStream in = null;
	String ret = "";
	byte[] bytes = new byte[length];
	try {
		in = new FileInputStream(file);
		in.read(bytes, 0, length);
		ret = getHexString(bytes);
	} catch (Exception e) {
		e.printStackTrace();
	} finally{
		if(in != null){
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	return ret;
}
  • 将字节数组转为16进制
public static String getHexString(byte[] bytes){
	if(bytes == null || bytes.length < 0){
		return null;
	}
	StringBuilder sb = new StringBuilder();
	for(byte b : bytes){
		int n = b & 0xFF;
		String str = Integer.toHexString(n);
		if(str.length() < 2){
			sb.append("0");
		}
		sb.append(str);
	}
	return sb.toString();
}
  • Example

folder目录下是一些没有后缀的文件,将此目录中可识别文件类型的文件拷贝到指定的目录

public static void disposeFolder(File folder){
	if(folder.isDirectory()){
		for(File file : folder.listFiles()){
			if(file.isDirectory()){
				disposeFolder(file);
			}else{
				//typeMap中列出的文件类型读取5个字节就够了
				String header = getFileHeader(file, 5);
				String type = getFileType(header);
				if(type != null){
					try {
						//import org.apache.commons.io.FileUtils;
						FileUtils.copyFile(file, new File(DEST_PATH + System.currentTimeMillis() + type));
					} catch (IOException e) {
						e.printStackTrace();
					}
				}else{
					System.err.println("unknown file: " + file.getAbsolutePath());
				}
			}
		}
	}
}

三、微信批量保存图片视频

对于手机中的聊天视频和图片,我一般是隔一段时间统一保存一次。更新6.6.5之后,没法在手机批量选择了,一次只能选9张保存,图片很多的话处理起来很麻烦,于是想直接从手机中微信的目录下找,结果如下:

  • 微信相关的目录在tencent/MicroMsg目录下

个人相关的文件在一个文件名类似UUID去掉-的文件中:

  • 图片、视频和语音

    • 图片

      图片在imageimage2sns目录下,在这些文件夹中搜.jpg结尾的即可;从sns目录中的没有文件名的文件中还可以恢复出之前在朋友圈看过的图片。

    • 视频

      视频在video目录下,此目录中包含视频的poster(预览图)图片;在此文件夹中搜.mp4结尾的文件即可。

    • 语音

      voice2目录下搜出了一些.amr文件,但是(使用AMP Player及其他播放软件)无法播放。

  • .nomedia

在扫描这些文件时,发现有很多文件夹下有.nomedia文件,于是上网搜了下,此文件是用来屏蔽媒体软件扫描的,拥有此文件的目录下的图片不会被图片浏览应用显示。

参考资料

https://www.cnblogs.com/13ck/p/4471146.html