一、如何获取真实文件格式?
当把文件的二进制数据转换成十六进制时,同类型文件的文件头数据是相同的,即使改变了其后缀,这个数据也不会改变。文件头是位于文件开头的一段承担一定任务的数据,一般在开头部分。头文件作为一种包含功能函数、数据接口声明的载体文件,用于保存程序声明(declaration),而定义文件用于保存程序的实现(implementation)。一般文件头开始几节数据是固定的,被称为魔数,作为区分文件类型的硬添加进去的数据。(例如PNG格式的十六进制文件头数据就是以"89504E47"开头的,就可以判定该文件是PNG格式)为了解决在用户上传文件的时候在服务器端判断文件类型的问题,故用获取文件头的方式,直接读取文件的前几个字节,来判断文件的真实类型。
————————————————
- 自定义魔数文件类型(可根据产品经理提供的业务格式进行定义)
public enum FileType {
/**
* JEPG.
*/
JPEG("FFD8FF"),
/**
* PNG.
*/
PNG("89504E47"),
/**
* GIF.
*/
GIF("47494638"),
/**
* TIFF.
*/
TIFF("49492A00"),
TXT("6C657420"),
/**
* Windows Bitmap.
*/
BMP("424D"),
/**
* CAD.
*/
DWG("41433130"),
/**
* Adobe Photoshop.
*/
PSD("38425053"),
/**
* Rich Text Format.
*/
RTF("7B5C727466"),
/**
* XML.
*/
XML("3C3F786D6C"),
/**
* HTML.
*/
HTML("68746D6C3E"),
/**
* Email.
*/
EML("44656C69766572792D646174653A"),
/**
* Outlook Express.
*/
DBX("CFAD12FEC5FD746F"),
/**
* Outlook (pst).
*/
PST("2142444E"),
/**
* MS Word/Excel.
*/
XLS_DOC("D0CF11E0"),
/**
* MS Access.
*/
MDB("5374616E64617264204A"),
/**
* WordPerfect.
*/
WPD("FF575043"),
/**
* Postscript.
*/
EPS("252150532D41646F6265"),
/**
* Adobe Acrobat.
*/
PDF("255044462D312E"),
/**
* Quicken.
*/
QDF("AC9EBD8F"),
/**
* Windows Password.
*/
PWL("E3828596"),
/**
* ZIP Archive.
*/
ZIP("504B0304"),
/**
* RAR Archive.
*/
RAR("52617221"),
/**
* Wave.
*/
WAV("57415645"),
/**
* AVI.
*/
AVI("41564920"),
/**
* Real Audio.
*/
RAM("2E7261FD"),
/**
* Real Media.
*/
RM("2E524D46"),
/**
* MPEG (mpg).
*/
MPG("000001BA"),
/**
* Quicktime.
*/
MOV("6D6F6F76"),
/**
* Windows Media.
*/
ASF("3026B2758E66CF11"),
GZ("1F8B08"),
/**
* MIDI.
*/
MID("4D546864");
private String value = "";
/**
* Constructor.
*
* @param value
*/
private FileType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
- FileUtil
@Service
public class FileUtil {
/**
* @param inputStream
* @throws IOException
* @description 第一步:获取文件输入流
*/
private static String getFileContent(InputStream inputStream) throws IOException {
byte[] b = new byte[20];
try {
inputStream.read(b, 0, 20);
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
}
return bytesToHexString(b);
}
/**
* @param
* @return 16进制字符串
* @description 第二步:将文件头转换成16进制字符串
*/
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* @param inputStream
* @return 文件类型
* @description 第三步:根据十六进制字符串判断文件类型格式
*/
public FileType getType(InputStream inputStream) throws IOException {
String fileHead = getFileContent(inputStream);
if (fileHead == null || fileHead.length() == 0) {
return null;
}
fileHead = fileHead.toUpperCase();
FileType[] fileTypes = FileType.values();
for (FileType type : fileTypes) {
// startsWith() 方法用于检测字符串是否以指定的前缀开始
if (fileHead.startsWith(type.getValue())) {
return type;
}
}
return null;
}
}
- upload (在上传业务体系中调用获取类型)
String fileFormat = fileUtil.getType(typeIs).name();
String suffixName = null;
//获取类型流
final InputStream typeIs = file.getInputStream();
String fileOrgName = file.getOriginalFilename();
String fileFormat = fileUtil.getType(typeIs).name();
if (StringUtils.isEmpty(fileFormat)) {
suffixName = fileOrgName.substring(fileOrgName.lastIndexOf("."));
} else {
//获取魔数类型
suffixName = "." + fileFormat.toLowerCase();
}
String uid = minioTemplate.randomUUID();
String fileName = uid + suffixName;