为了实现一个文件分片上传方法,找了很久的demo,但是一直没有找到,最后找到了一个plupload插件,可以支持文件分片。但是,第一次接触这个插件,有很多不会用的地方,网上的许多文章毕竟是他们做的,拿来当然会有许多坑,一步一步调试再理解插件的作用,最后成功。这个插件中,有许多功能,但我最主要的是用了插件的分片的功能,文件类型验证全部由后端验证。
plupload插件的使用方法在csdn、博客园、开源中国论坛都有很多讲的,在这里给一个我觉得介绍挺完整的:https://www.cnblogs.com/2050/p/3913184.html
下面这个是官方文档:http://www.phpin.net/tools/plupload/
"uploader">
"listFile.jsp">下载
File dir = new File(request.getSession().getServletContext().getRealPath("/") + FileDir+"/"+userId);
if(!dir.exists()){
//可创建多级目录,而mkdir()只能创建一级目录
dir.mkdirs();
}
//开始上传文件
PluploadService.upload(plupload,fileName,dir);
System.out.println("上传成功,路径为:"+dir);
}catch (Exception e){
e.printStackTrace();
}
return "message";
在这个类中我进行了文件类型判断,由于文件是分片上传的,服务器得到的文件是一块一块的,所以很难进行截取完整的文件头。因此,我多加了一个判断,文件是第一块的时候,截取文件头,获取文件类型从而进行判断。如果文件类型不允许,则直接返回。
private static long UPLOAD_MAXSIZE = 800 * 1024 * 1024;
public static void upload(Plupload plupload,String fileName, File pluploadDir) throws ServletException, IOException {
String name = "" + System.currentTimeMillis()+fileName;
uploads(plupload,name, pluploadDir);
}
/**
* 上传方法
* @param plupload 上传实体对象
* @param pluploadDir 上传的文件夹名称
* @param fileName 保存的文件名
*/
private static void uploads(Plupload plupload,String fileName, File pluploadDir) {
boolean flag=false;
System.out.println(plupload);
//用户上传文件被分隔的总块数
int chunks = plupload.getChunks();
//当前块,从0开始
int nowChunk = plupload.getChunk();
System.out.println("当前块:"+nowChunk+"\n");
//这里Request请求类型的强制转换可能出错,配置文件中向SpringIOC容器引入multipartResolver对象即可。
MultipartHttpServletRequest mulRequest = (MultipartHttpServletRequest) plupload.getRequest();
//map中只有一个键值对
MultiValueMap map = mulRequest.getMultiFileMap();
if (map != null) {
try {
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
List multipartFileList = map.get(key);
//循环只进行一次
for (MultipartFile multipartFile : multipartFileList) {
//手动向Plupload对象传入MultipartFile属性值
plupload.setMultipartFile(multipartFile);
//新建目标文件,只有被流写入时才会真正存在
File targetFile = new File(pluploadDir + "/" + fileName);
//当前块为首块时,截取文件头进行判断
if(nowChunk==0){
if (!creatCheckFile(multipartFile, plupload.getRequest(),fileName)){
return;
}
}
//上传资料总块数大于1,要进行合并
if (chunks >1) {
File tempFile = new File(pluploadDir.getPath() + "/" + multipartFile.getName());
//第一块直接从头写入,不用从末端写入
savePluploadFile(multipartFile.getInputStream(), tempFile, nowChunk == 0 ? false : true);
//全部块已经上传完毕,此时targetFile因为有被流写入而存在,要改文件名字
if (chunks - nowChunk == 1) {
flag=true;
tempFile.renameTo(targetFile);
}
}else {
multipartFile.transferTo(targetFile);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
}
public static boolean creatCheckFile(MultipartFile file, HttpServletRequest request,String fileName) throws ServletException, IOException {
boolean flag = false;
//获取文件名
String message = "";
//判断文件不为空
if (file.getSize() != 0 && !file.isEmpty()) {
flag = true;
//判断文件大小
if (file.getSize() <= UPLOAD_MAXSIZE) {
flag = true;
//文件类型判断
if (CheckFileType.getCheckFile(file,fileName)) {
flag = true;
} else {
flag = false;
System.out.println("文件格式不符合!");
message = "文件格式不符合";
}
} else {
flag = false;
System.out.println("文件大小超出范围!");
message = "文件大小超出范围!";
}
} else {
flag = false;
System.out.println("文件为空!");
message = "文件为空!";
}
return flag;
}
/**
* 保存文件
* @param is 文件输入流
* @param tempFile 临时文件
* @param flag 操作标识
*/
private static void savePluploadFile(InputStream is, File tempFile, boolean flag){
OutputStream os=null;
try{
//从末端写入
if(tempFile.exists()){
os=new BufferedOutputStream(new FileOutputStream(tempFile,true));
}else {//从头写入
os=new BufferedOutputStream(new FileOutputStream(tempFile));
}
byte []bytes=new byte[1024*1024*20];
int len=0;
while ((len=(is.read(bytes)))>0){
os.write(bytes,0,len);
}
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
os.close();
is.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
截取八位以上文件头,文件头会发生一点改变,截取八位文件头,部分文件的文件头是一样的。在这里截取的是前八位文件头。
/**
* 判断上传的文件是否合法
* 1.检查文件的扩展名
* 2.检查文件的MIME类型
*
* @param file
* @return boolean
*/
public final static boolean getCheckFile(MultipartFile file, String fileName) {
//为真表示符合上传条件,为假表标不符合
boolean upflag = false;
String sname = fileName.substring(fileName.lastIndexOf(".") + 1);
//转换成小写
sname = sname.toLowerCase();
System.out.println("上传文件后缀:" + sname);
//获取上传附件的文件头,判断属于哪种类型,并获取其扩展名
byte[] b = new byte[4];
try {
InputStream is = file.getInputStream();
is.read(b, 0, b.length);
String fileHeader = getFileHeader(b);
System.out.println("上传文件的文件头为:" + fileHeader);
if (FILE_TYPE_MAP.containsKey(fileHeader)) {
upflag = true;
//检查文件扩展名
String va = FILE_TYPE_MAP.get(fileHeader);
System.out.println("文件的扩展名应为:" + va);
if (va.equals(sname)) {
upflag = true;
} else {
//特殊情况校验,有的文件头前八位
if (elseCheck(sname,fileHeader)){
upflag=true;
}
//特殊情况校验,如果用户上传的扩展名为文本文件,则允许上传
if (sname.indexOf("txt") != -1) {
return true;
} else {
return false;
}
}
is.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return upflag;
}
/**
* 获取上传文件类型
*
* @param b
* @return
*/
public final static String getFileHeader(byte[] b) {
String value = getFileHexString(b);
return value;
}
/**
* 读取文件头,将字节数组的前四位转换成十六进制
*
* @param b 读取文件头信息的文件的byte数组
* @return 文件头信息
*/
public final static String getFileHexString(byte[] b) {
StringBuilder builder = new StringBuilder();
if (b == null || b.length <=0) {
return null;
}
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
builder.append(0);
}
builder.append(hv);
}
return builder.toString();
}
private static boolean elseCheck(String suffix,String fileHeader){
boolean check=false;
//扩展名为"wav"与"avi"的前八位文件头相同
if ("wav".equals(suffix)&& "52494646".equals(fileHeader)){
return true;
}
//扩展名为"f4v"与"flv"的文件头相同
if ("f4v".equals(suffix) && "464c5601".equals(fileHeader)){
return true;
}
//扩展名为"rmvb"与"rm"的文件头相同
if ("rm".equals(suffix)&& "2e524d46".equals(fileHeader)){
return true;
}
//扩展名为"asf"与"wmv"的文件头相同
if ("asf".equals(suffix)&&"3026b275".equals(fileHeader)){
return true;
}
//jar与zip只是前八位相同
for (String s:wpsSame) {
if ( suffix.equals(s)&& "504b0304".equals(fileHeader)){
return true;
}
}
for (String s:docSame) {
if ( suffix.equals(s)&&"d0cf11e0".equals(fileHeader)){
return true;
}
}
return check;
}
Plupload类名不能改变,除非在plupload源码插件中改变。在这里,没有给出允许文件类型的集合,这个可以自己定义。
public class Plupload {
/**文件原名*/
private String name;
/**用户上传资料被分解总块数*/
private int chunks = -1;
/**当前块数(从0开始计数)*/
private int chunk = -1;
/**HttpServletRequest对象,不会自动赋值,需要手动传入*/
private HttpServletRequest request;
private HttpServletResponse response;
/**保存文件上传信息,不会自动赋值,需要手动传入*/
private MultipartFile multipartFile;