java 上传文件注意事项
1、文件名有特殊字符的情况,所以最好是文件名前台url编码,后台再url解码,这点在下载的时候也一样 2、文件大小一定要设置,spring boot 有默认。 3、文件名校验: 3.1、文件后缀校验 3.2、content-type校验 3.3、通过文件流,校验文件头,真实校验
其中,单纯的文件后缀和content-type校验,容易被绕过,只有文件流才能真实的判断出这个文件是什么类型的。 一般的做法就是先通过后缀过滤,绝大部分请求还是正常人 然后再通过流截取文件头信息,判断文件的真实类型。
特别注意: txt文本文档时没有头的,所以不能通过判断头来过滤,以下是网友的一些建议,摘过来供参考:
bmp,jpg等图象文件,可以判断文件头,来确定它的文件类型
但txt没有文件头的,无法判断它是否是txt文件
所以,只能分析文件中所有数据,看看是不是都是可以显示的字符
如果都是可以显示的字符,说明它可以当作txt文件进行阅读,否则,会有乱码出现
这显然要先了解一下什么是文本类的文件,其实说白了,就是ACSII码文件。二进制文件与文本文件的显著差别在于:二进制文件中可能会出现数目众多的0,而文本文件中则不会有,因为文本文件中除了一些特殊的控制字符(比如\n,\r,\t)以外,都是可见字符。
补充1:
除了.txt文件以外,其它的各种文件似乎都会有二进制文件头,这些文件头都很特别,比如0xFEFE、0xFDFD等等。必须说明的一点是.doc文件严格的说,它不应该是一个文本文件,因为它内部有大量的不可见的控制字符,而且最关键的是,它内部允许包含0,并且它有二进制的头数据块。
如果你真想鉴别所有的,你所期望的文本类文件的话,比如.txt、.doc这些文件,你就必须了解这些文件的文件格式,当然.txt是没有固定格式的,但它有一个特征是,不会含有0。
补充2:
对于unicode的文本文件来说,它也是有文件头的,根据大小尾的不同,分别是FFFE和FEFF,严格的说,unicode的文件,不能说是文本文件。
回到你的补充问题,如果想确认一个文件是不是文本文件,加入说是ANSI的,你可以遍历整个文件,看是否存在0,如果没有那么就是了,如果具有FFFE或者FEFF文件头的unicode文件,那么你所关注的对象是,00,也就是两个连续的0。
个人认为, 要么就是把所有可能的文件后缀都放在那个判断的map中(下面代码中会初始化),然后再拿不到就认为是txt类型 要么就是遍历整个文件,看是否存在0,没有就是txt
下面是相关代码:
@Slf4j
public class FileCheckUtil {
private static final HashMap
<String, String> MFILETYPES
= new HashMap<String, String>();
static {
MFILETYPES
.put("25504446", "pdf");
MFILETYPES
.put("255044462D312E", "pdf");
MFILETYPES
.put("D0CF11E0", "xls");
MFILETYPES
.put("504B0304", "xlsx");
MFILETYPES
.put("FFD8FFE0", "jpg");
MFILETYPES
.put("FFD8FF", "jpg");
MFILETYPES
.put("89504E47", "png");
MFILETYPES
.put("47494638", "gif");
MFILETYPES
.put("49492A00", "tif");
MFILETYPES
.put("424D", "bmp");
MFILETYPES
.put("41433130", "dwg");
MFILETYPES
.put("57415645", "wav");
MFILETYPES
.put("41564920", "avi");
MFILETYPES
.put("2E524D46", "rm");
MFILETYPES
.put("000001BA", "mpg");
MFILETYPES
.put("000001B3", "mpg");
MFILETYPES
.put("6D6F6F76", "mov");
MFILETYPES
.put("3026B2758E66CF11", "asf");
MFILETYPES
.put("38425053", "psd");
MFILETYPES
.put("7B5C727466", "rtf");
MFILETYPES
.put("3C3F786D6C", "xml");
MFILETYPES
.put("68746D6C3E", "html");
MFILETYPES
.put("44656C69766572792D646174653A", "eml");
MFILETYPES
.put("5374616E64617264204A", "mdb");
MFILETYPES
.put("252150532D41646F6265", "ps");
MFILETYPES
.put("52617221", "rar");
MFILETYPES
.put("4D546864", "mid");
MFILETYPES
.put("1F8B08", "gz");
}
public static String
getRealFileType(FileInputStream is
) {
return MFILETYPES
.get(getFileHeader(is
));
}
public static String
getFileHeader(FileInputStream is
) {
String value
= null
;
try {
byte[] b
= new byte[4];
int read
= is
.read(b
, 0, b
.length
);
log
.debug("read:{}", read
);
value
= bytesToHexString(b
);
} catch (Exception e
) {
} finally {
if (null
!= is
) {
try {
is
.close();
} catch (IOException e
) {
}
}
}
return value
;
}
private static String
bytesToHexString(byte[] src
) {
StringBuilder builder
= new StringBuilder();
if (src
== null
|| src
.length
<= 0) {
return null
;
}
String hv
;
for (int i
= 0; i
< src
.length
; i
++) {
hv
= Integer
.toHexString(src
[i
] & 0xFF).toUpperCase();
if (hv
.length() < 2) {
builder
.append(0);
}
builder
.append(hv
);
}
log
.debug("builder.toString()={}", builder
.toString());
return builder
.toString();
}
public static void main(String
[] args
) throws Exception
{
}
}
正式的controller:
private static final List
<String> FILETYPELIST
= Arrays
.asList("txt", "doc", "docx", "xls", "xlsx", "pdf");
@PostMapping(value
= "/upload")
public BaseResponseDTO
uploadFile(@RequestParam("file") MultipartFile file
) throws IOException
{
BaseResponseDTO data
= checkFile(file
);
if (data
!= null
) {
return data
;
}
String originalFilename
= file
.getOriginalFilename();
long now
= System
.currentTimeMillis();
originalFilename
= now
+ "_" + originalFilename
;
String filePath
= "/app/upload/";
String path
= filePath
+ originalFilename
;
File dest
= new File(path
);
file
.transferTo(dest
);
return new BaseResponseDTO<>(HTTPState
.OK
.getCode(), HTTPState
.OK
.getMsg(), originalFilename
);
}
private String
checkFile(MultipartFile file
) throws IOException
{
if (file
.isEmpty()) {
return "error";
}
String fileName
= file
.getOriginalFilename();
if (fileName
!= null
) {
String suffix
= fileName
.substring(fileName
.lastIndexOf(".") + 1);
log
.debug("suffix:" + suffix
);
if (!FILETYPELIST
.contains(suffix
)) {
return "error";
}
}
String fileType
= FileCheckUtil
.getRealFileType((FileInputStream
) file
.getInputStream());
log
.debug("fileType:" + fileType
);
if (fileType
!= null
&& !FILETYPELIST
.contains(fileType
)) {
return "error";
}
return null
;
}