点击蓝色“Java极客技术”关注我哟
加个“星标”,一起快乐成长
01、故事背景
试想一下你现在有个业务场景需要你识别出各种类型的文件,然后进行不同的处理,这些文件不管是用户上传还是怎么来的,你都需要知道文件是什么类型,比如是一张 JPG 图片,还是一个 GIF 图片又或者是 PDF 等等其他类型的文件。这个时候你会想,这还不简单么,几行 Java 代码就搞定了。
如下:
public static String getFileTypeByExt(String filePath) {
if (StringUtils.isBlank(filePath)) {
return null;
}
return filePath.toLowerCase().substring(filePath.lastIndexOf(".") + 1);
}
敲完过后,满心欢喜,这个需求简单的无法用言语去描述,提交了代码,更新到测试环境去了,优雅又不是风度的等着下班。
眼看马上到下班的点了,起身收拾东西准备下班,正在你关上电脑的那一刹那,说时迟那时快,测试人员突然跑过来说有个 bug,需要你看下。此时的你脑海中浮现了如下场景:
bug?怎么可能,我写的代码怎么可能有 bug?
什么 bug?肯定是你不会用!
肯定没清缓存吧(但是我改的后台代码,好像不影响浏览器缓存啊,摸了下后脑勺~)
不会真有 bug 吧~~
几种情况快速的在你大脑中闪现过,但是你很自信的认为是测试人员不会用,不过你没有表现出来,淡定并且很有礼貌的说了句:哦,是么,我跟你过去看下。
走到测试人员的工位前,看着桌上有一个 jpg 图片,点击按钮上传,选中这个图片,开始上传,一顿操作猛如虎,正在准备大功告成的时候,系统弹出了一个“系统错误”的弹框出来,登上测试服务器看了日志信息,提示在处理图片的时候相关类库提示失败了。
咦,不科学啊,不会是真的,清下缓存,刷新下页面,循环重复了几次操作之后,结果还是一样的,这个时候你有点认为这是个 bug 了。但是自己回想了一下差不多半个小时前写的代码,怎么都不会有问题啊,要不换个图片试试?
就这样抱着试一试的想法,换了一张图片,惊喜的发现成功了,没有提示系统错误,正常的上传成功了。看到这个现象,内心欢喜了一下,原来是图片的问题,就说不是 bug 了。
但是等等,为什么换个图片可以,这个图片就不行呢?仔细看了一个原来的图片文件,没发现什么异常啊,就是图片的大小差不多 5M,换的图片是随便截图的只有几百 k,难道是大小的原因?但是不对啊,系统是支持上传PDF 文件的,所以大小是可以超过 5M 的啊。
咦,等等这个文件的大小怎么跟我刚刚发的 PDF 的大小一样啊,之前测试文档上传,发了一个 PDF 给测试人员,大小差不多就是这个,怎么也有一个这样大小的图片呢?然后随口问了句:你这个图片哪来的啊?只见测试人员淡定的说到:哦,我是把刚刚的 PDF 文件的后缀改成了 JPG,所以这个图片不是真的图片,我只是改了一下后缀。
听到这句话的时候你的内心是崩溃的,但是你没有表现出来,毕竟测试人员的职责就是维护系统的稳定性。
故事发生到这里,你已经知道不能使用上面那段优雅的代码了,需要考虑其他方案,简单的根据后缀名来判断文件类型进行处理是不行的了。
此时的你陷入了深深的沉思当中。。。
在走回工位的路上拿出手机给媳妇(假如有的话)发了个微信:今晚加班,不回去吃饭了。
02、解决方案
为了解决这个问题,在面向搜索引擎编程的年代,你在网上搜到了一个叫做魔数的东西(本文的重点来了),抱着试一试的想法,实践起来了。
2.1、魔数的定义
魔数有很多种定义,这里我们讨论的主要是在编程领域的定义
文件的起始几个字节的内容是固定的(或是有意填充,或是本就如此),这几个字节的内容也被称为魔数 (magic number),因此可以根据这几个字节的内容确定文件类型。
2.2、常见文件类型的魔数
package com.coocaa.ad.caeser.engine.com.test.model;
/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2019-10-24 11:21<br>
* <b>Desc:</b>无<br>
*/
public enum FileType {
/**
* JPEG
*/
JPEG("JPEG", "FFD8FF"),
/**
* PNG
*/
PNG("PNG", "89504E47"),
/**
* GIF
*/
GIF("GIF", "47494638"),
/**
* TIFF
*/
TIFF("TIFF", "49492A00"),
/**
* Windows bitmap
*/
BMP("BMP", "424D"),
/**
* CAD
*/
DWG("DWG", "41433130"),
/**
* Adobe photoshop
*/
PSD("PSD", "38425053"),
/**
* Rich Text Format
*/
RTF("RTF", "7B5C727466"),
/**
* XML
*/
XML("XML", "3C3F786D6C"),
/**
* HTML
*/
HTML("HTML", "68746D6C3E"),
/**
* Outlook Express
*/
DBX("DBX", "CFAD12FEC5FD746F "),
/**
* Outlook
*/
PST("PST", "2142444E"),
/**
* doc;xls;dot;ppt;xla;ppa;pps;pot;msi;sdw;db
*/
OLE2("OLE2", "0xD0CF11E0A1B11AE1"),
/**
* Microsoft Word/Excel
*/
XLS_DOC("XLS_DOC", "D0CF11E0"),
/**
* Microsoft Access
*/
MDB("MDB", "5374616E64617264204A"),
/**
* Word Perfect
*/
WPB("WPB", "FF575043"),
/**
* Postscript
*/
EPS_PS("EPS_PS", "252150532D41646F6265"),
/**
* Adobe Acrobat
*/
PDF("PDF", "255044462D312E"),
/**
* Windows Password
*/
PWL("PWL", "E3828596"),
/**
* ZIP Archive
*/
ZIP("ZIP", "504B0304"),
/**
* ARAR Archive
*/
RAR("RAR", "52617221"),
/**
* WAVE
*/
WAV("WAV", "57415645"),
/**
* AVI
*/
AVI("AVI", "41564920"),
/**
* Real Audio
*/
RAM("RAM", "2E7261FD"),
/**
* Real Media
*/
RM("RM", "2E524D46"),
/**
* Quicktime
*/
MOV("MOV", "6D6F6F76"),
/**
* Windows Media
*/
ASF("ASF", "3026B2758E66CF11"),
/**
* MIDI
*/
MID("MID", "4D546864");
private String key;
private String value;
FileType(String key, String value) {
this.key = key;
this.value = value;
}
public String getValue() {
return value;
}
public String getKey() {
return key;
}
}
2.3、工具类
package com.test.util;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2019-10-24 11:16<br>
* <b>Desc:</b>无<br>
*/
public class FileUtil {
/**
* 获取文件投
*
* @param filePath 文件路径
* @return 16 进制的文件投信息
* @throws IOException
*/
private static String getFileHeader(String filePath) throws IOException {
byte[] b = new byte[28];
InputStream inputStream = new FileInputStream(filePath);
inputStream.read(b, 0, 28);
inputStream.close();
return bytes2hex(b);
}
/**
* 将字节数组转换成16进制字符串
*/
private static String bytes2hex(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (byte b : src) {
int v = b & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* 根据文件路径获取文件类型
*
* @param filePath 文件路径
* @return 文件类型
* @throws IOException
*/
public static FileType getFileType(String filePath) throws IOException {
String fileHead = getFileHeader(filePath);
if (null == fileHead || fileHead.length() == 0) {
return null;
}
fileHead = fileHead.toUpperCase();
FileType[] fileTypes = FileType.values();
for (FileType type : fileTypes) {
if (fileHead.startsWith(type.getValue())) {
return type;
}
}
return null;
}
}
2.4、使用
public static void main(String[] args) throws IOException {
// String filePath = "/Users/ziyou/Downloads/SpringBoot实战.pdf";
String filePath = "/Users/ziyou/Downloads/SpringBoot实战.png";
FileType fileType = getFileType(filePath);
System.out.println(fileType.getKey());
}
上面的输出为PDF,说明我们的实验是成功的,所以我们可以根据魔数在校验文件的类型了。
在经过一顿操作猛如虎之后,看着屏幕的代码,你感到无比的欣慰,原来一个如此简单的需求也是要考虑很多细节的,不能太随意。
提交了代码,关上了电脑,想着自己加班这么晚,真是累,但是看着窗外灯火通明,路上车水马龙,知道还有很多人跟你一样在奋斗,瞬间又有了动力~
03、总结
今天通过一个真实的业务场景给大家介绍了编程中魔数的相关应用,希望能帮助到大家。
另外从另一个方面也告诉了我们在遇到一个需求的时候,需要的考虑一点,有时候并不是自己想的那样,需要从一开始就考虑的多一点,这样后面就会省心好多。
最后欢迎大家到我们《Java 极客技术》知识星球中一起谈论技术人生,互相成长,互相进步。
精彩回顾: