WebUploader官网介绍:
WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。
采用大文件分片并发上传,极大的提高了文件上传效率。
背景:
在一个风和日丽的下午,我在我的办公桌上悠然的划水摸鱼。突然总监走过来,虽然有点突然但是在我的熟练的手法上一个alt+tab马上就切换到了工作界面。一脸笑意的迎接我总监并问道什么事。跟我说最近公司项目要做一个秒上传和大文件上传的功能叫我自己在网上选一个插件,跟前端大哥来实现这个功能。然后我就选择了这一个百度的webUploader国产插件。
秒上传和大文件分片上传的思路:
在做项目之前我觉得思路是很重要的,所以我在做这个功能的时候我阅读了很多的文章;让我有了明确的思路,希望不足之处能得到大佬的指点,如果对你们有所帮助的话,我们可以一起学习呀!
秒上传思路:
在文件上传的时候,WebUploader这个插件会在文件添加到上传文件的列队的时候会把文件的md5值计算出来。然后把md5值返回给我后台来验证判断服务器上是否存在这个文件,如果存在就将跳过上传 直接返回上传成功(秒传) 然后再服务器上找到这个文件。再要上传的目标地址创建这个文件的快捷方式。
大文件分片上传的思路:
在文件大小超过100m的时候就会把文件进行切割,每一片的大小由前端插件来进行设置。在这里我设置的是100M.然后分片上传到后台来。我创建一个临时的文件夹来接收这些分片的文件,当分片全部上传完的时候,把这些分片文件合并起来并输出到指定的路径当中。然后把临时文件夹删除掉
md5验证的代码段落:
/**
* 判断是否存在md5
*
* @throws IOException
*/
@RequestMapping(value = "/isFindMd5", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
public HashMap<String, String> isFindMd5(HttpServletRequest request, HttpServletResponse response,
@RequestParam("md5") String md5, @RequestParam("uid") String uid) throws IOException {
File fileEntity = new File();
HashMap<String, String> hashMap = new HashMap<>();
File file = new File();
StringBuilder pathP = new StringBuilder();
FileContentInfo fileContentInfo = new FileContentInfo();
// 获取文件要存储在本地磁盘的路径
if (uid != null) {
fileEntity = fileService.get(uid);
Folder folder = fileService.getFolderFileListFolder(fileEntity);
String parentIds = folder.getParentIds();
pathP = getPath(parentIds);
pathP.append("\\").append(folder.getName());
}
// 上传文件到数据库
fileContentInfo.setUid(IdGenerator.uuid());
fileContentInfo.setStoragePath(String.valueOf(pathP));
File fileFileContentInf = new File();
fileContentInfo.setContentHash(md5);
file.setFileContentInfo(fileContentInfo);
List<FileContentInfo> fileContentInfos = fileService.getFileContentInfo(file);// 获取是否有相同的md5的值的文件
// 修改file把上传文件详细信息id关联上
fileEntity.setContentInfoId(fileContentInfo.getUid());
fileEntity.setIsNewRecord(false);
fileService.save(fileEntity);
//判断是否存在md5值如果有进行秒传,跳过上传,不存在将进行正常的上传
if (fileContentInfos.size() > 0) {
FileContentInfo fContentInfo = fileContentInfos.get(0);
File entity = new File();
entity.setContentInfoId(fContentInfo.getUid());
List<File> files = fileService.getList(entity);
File srcfile = files.get(0);
String truePaht = "D:\\jtsUploadFile" + "\\" + getDateFileName() + "\\" + fContentInfo.getContentHash()
+ "." + srcfile.getFileExtension();
// 快捷方式路径
String shortcutsPaht = fileContentInfo.getStoragePath() + "\\" + fileEntity.getName() + "."
+ fileEntity.getFileExtension();
// 创建快捷方式
FileUtill.createShortCut(truePaht, shortcutsPaht, null);
fileContentInfo.setFileSize(fContentInfo.getFileSize());
// 保存
fileFileContentInf.setFileContentInfo(fileContentInfo);
fileService.insertsFileContentInfMiddleTable(fileFileContentInf);
hashMap.put("date", "文件已存在,进行秒上传");
hashMap.put("msg", "存在");
return hashMap;
} else {
// 保存
fileFileContentInf.setFileContentInfo(fileContentInfo);
fileService.insertsFileContentInfMiddleTable(fileFileContentInf);
hashMap.put("date", "文件不存在,进行正常流程");
hashMap.put("msg", "不存在");
return hashMap;
}
}
文件上传和大文件上传代码:
在文件超过设置的文件大小的时候,文件会进行切割。并且会自动传到后台来个参数过来chunks:分片的总数 chunk:当前第几个分片。我就是通过这个来判断是不是大文件
/**
* 文件列表上传
*
* @throws IOException
*/
@SuppressWarnings("unused")
@RequestMapping(value = "/fileUpload", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
public JtsResult fileUpload(HttpServletRequest request, HttpServletResponse response,
@RequestParam("upload") MultipartFile file, @RequestParam("uid") String uid,
@RequestParam(value = "chunks", required = false) String chunks,
@RequestParam(value = "chunk", required = false) String chunk) throws IOException {
//判断文件是否分片
if (chunks == null) {
File fileEntity = fileService.get(uid);
FileContentInfo fileContentInfo = fileService.getFileContentInfoById(fileEntity);
String uploadFileName = fileContentInfo.getContentHash() + "." + fileEntity.getFileExtension();
long size = file.getSize();
// 真实文件的路径
String truePaht = "D:\\jtsUploadFile" + "\\" + getDateFileName();
// 创建文件
mergFiles(file, truePaht, uploadFileName);
// 获取文件的信息
fileContentInfo.setFileSize((float) size);
// fileContentInfo.setContent(file.getBytes());
// 上传文件到数据库
File fileFileContentInf = new File();
fileFileContentInf.setFileContentInfo(fileContentInfo);
fileService.updateFileContentInfoMiddleTable(fileFileContentInf);
// 快捷方式路径
String shortcutsPaht = fileContentInfo.getStoragePath() + "\\" + fileEntity.getName() + "."
+ fileEntity.getFileExtension();
// 创建快捷方式
FileUtill.createShortCut(truePaht + "\\" + uploadFileName, shortcutsPaht, null);
} else {
File fileEntity = fileService.get(uid);
FileContentInfo fileContentInfo = fileService.getFileContentInfoById(fileEntity);
String uploadFileName = IdGenerator.uuid() + "_" + fileContentInfo.getContentHash() + "."
+ fileEntity.getFileExtension();
size += file.getSize();
int chunksInt = Integer.parseInt(chunks);
int chunkInt = Integer.parseInt(chunk);
fileContentInfo.setStoragePath("D:\\jtsmerge");
String shardPaht = fileContentInfo.getStoragePath();
mergFiles(file, shardPaht, uploadFileName);
if (chunksInt == chunkInt + 1) {
java.io.File file1 = new java.io.File("D:\\jtsmerge");
if (file1.isDirectory()) {
java.io.File[] files = file1.listFiles();
if (files != null && files.length > 0) {
FileContentInfo fileContentInfo2 = fileService.getFileContentInfoById(fileEntity);
java.io.File partFile = new java.io.File(fileContentInfo2.getStoragePath());
// 真实文件的路径
String trueFilename = fileContentInfo.getContentHash();
String trueFileName = fileContentInfo.getContentHash() + "." + fileEntity.getFileExtension();
String truePaht = "D:\\jtsUploadFile" + "\\" + getDateFileName() + "\\" + trueFileName;
for (int i = 0; i < files.length; i++) {
FileOutputStream destTempfos = new FileOutputStream(truePaht, true);
FileUtils.copyFile(files[i], destTempfos);
destTempfos.close();
}
// 获取最终合并之后的文件
java.io.File fileMysql = new java.io.File(truePaht);
// 获取文件的信息
fileContentInfo2.setFileSize((float) size);
// 上传文件到数据库
File fileFileContentInf = new File();
fileFileContentInf.setFileContentInfo(fileContentInfo2);
fileService.updateFileContentInfoMiddleTable(fileFileContentInf);
size = 0;
// 快捷方式路径
String shortcutsPaht = fileContentInfo2.getStoragePath() + "\\" + fileEntity.getName() + "."
+ fileEntity.getFileExtension();
// 创建快捷方式
FileUtill.createShortCut(truePaht + "\\" + uploadFileName, shortcutsPaht, null);
}
}
FileUtils.deleteDirectory(file1);
}
}
JtsResult result = JtsResultUtil.success("上传成功");
return result;
}
/**
* 文件合并
*
* @throws IOException
*
*
*/
public void mergFiles(MultipartFile file, String path, String uploadFileName) throws IOException {
java.io.File dirFile = new java.io.File(path, uploadFileName);
FileUtils.copyInputStreamToFile(file.getInputStream(), dirFile);
}
/**
* 获取当前的年月来做文件实际路径的第二级路径名
*
* @throws IOException
*/
public String getDateFileName() {
Calendar now = Calendar.getInstance();
String year = String.valueOf(now.get(Calendar.YEAR));
String month = (now.get(Calendar.MONTH) + 1) + "";
StringBuffer date = new StringBuffer(year);
date.append(month);
return String.valueOf(date);
}
// 获取文件夹路径
public StringBuilder getPath(String parentIds) {
StringBuilder pathP = new StringBuilder("D:\\jts");
List<Folder> fildFolders = new ArrayList<>();
if (parentIds != null) {
String[] parentFolders = parentIds.split(",");
List<Folder> folders = new ArrayList<>();
for (int i = 0; i < parentFolders.length; i++) {
if (!parentFolders[i].equals("0")) {
Folder fildFolder = new Folder();
fildFolder.setUid(parentFolders[i]);
folders.add(fildFolder);
}
}
fildFolders = folderService.selectgetFolder(folders);
for (Folder fildPath : fildFolders) {
pathP.append("\\").append(fildPath.getName());
}
}
return pathP;
}
FileUtil
package com.spatiomap.doc.util;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.springframework.web.multipart.MultipartFile;
import com.spatiomap.doc.entity.FileContentInfo;
import net.jimmc.jshortcut.JShellLink;
public class FileUtill {
// 删除文佳夹和里面所有的内容
public static String deleteFile(File file) {
// 判断文件不为null或文件目录存在
if (file == null || !file.exists()) {
return "error";
}
// 取得这个目录下的所有子文件对象
File[] files = file.listFiles();
// 遍历该目录下的文件对象
for (File f : files) {
// 打印文件名
String name = file.getName();
// 判断子目录是否存在子目录,如果是文件则删除
if (f.isDirectory()) {
deleteFile(f);
} else {
f.delete();
}
}
// 删除空文件夹 for循环已经把上一层节点的目录清空。
file.delete();
return "sceess";
}
/**
* 复制整个文件夹内容
*
* @param oldPath
* String 原文件路径 如:c:/fqf
* @param newPath
* String 复制后路径 如:f:/fqf/ff
**/
public static void copyFolder(String oldPath, String newPath) {
try {
(new File(newPath)).mkdirs(); // 如果文件夹不存在 则建立新文件夹
File a = new File(oldPath);
String[] file = a.list();
File temp = null;
for (int i = 0; i < file.length; i++) {
if (oldPath.endsWith(File.separator)) {
temp = new File(oldPath + file[i]);
} else {
temp = new File(oldPath + File.separator + file[i]);
}
if (temp.isFile()) {
FileInputStream input = new FileInputStream(temp);
FileOutputStream output = new FileOutputStream(newPath + "/" + (temp.getName()).toString());
byte[] b = new byte[1024 * 5];
int len;
while ((len = input.read(b)) != -1) {
output.write(b, 0, len);
}
output.flush();
output.close();
input.close();
}
if (temp.isDirectory()) {// 如果是子文件夹
copyFolder(oldPath + "/" + file[i], newPath + "/" + file[i]);
}
}
} catch (Exception e) {
System.out.println("复制整个文件夹内容操作出错");
e.printStackTrace();
}
}
/**
* 将文件转换成byte数组
*
* @param filePath
* @return
*/
public static byte[] FileConversionbyte(File tradeFile) {
byte[] buffer = null;
try {
FileInputStream fis = new FileInputStream(tradeFile);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
/**
* 文件合并
*
* @throws IOException
*
*
*/
public static void mergFiles(MultipartFile file, FileContentInfo fileContentInfo, String uploadFileName)
throws IOException {
boolean flag = true;
// 合并文件
RandomAccessFile raFile = null;
BufferedInputStream inputStream = null;
try {
java.io.File dirFile = new java.io.File(fileContentInfo.getStoragePath(), uploadFileName);
// 以读写的方式打开目标文件
raFile = new RandomAccessFile(dirFile, "rw");
raFile.seek(raFile.length());
inputStream = new BufferedInputStream(file.getInputStream());
byte[] buf = new byte[1024];
int length = 0;
while ((length = inputStream.read(buf)) != -1) {
raFile.write(buf, 0, length);
}
} catch (Exception e) {
flag = false;
throw new IOException(e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (raFile != null) {
raFile.close();
}
} catch (Exception e) {
flag = false;
throw new IOException(e.getMessage());
}
}
}
/**
* 创建快捷方式
* @param fileorFolderPath 源文件夹路径
* @param writeShortCutPath 目标文件路径(快捷方式型)
*/
public static void createShortCut(String fileorFolderPath, String writeShortCutPath, String arguments){
JShellLink link=new JShellLink();
writeShortCutPath=writeShortCutPath.replaceAll("/", "\\");
String folder=writeShortCutPath.substring(0, writeShortCutPath.lastIndexOf("\\")+1);
String name=writeShortCutPath.substring(writeShortCutPath.lastIndexOf("\\")+1,
writeShortCutPath.length());
link.setName(name);//目标快捷方式文件夹名称
link.setFolder(folder);//目的快捷方式文件路径片段
link.setPath(fileorFolderPath);
if(arguments != null && !"".equals(arguments.trim())){
link.setArguments(arguments);
}
link.save();
}
}
FileUtils:
我是使用的的org.apache.commons.io.FileUtils这是一款非常好用的FIle操作工具类maven工程需要添加一下依赖
<!-- FileUtils工具类 -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.io</artifactId>
<version>2.4</version>
</dependency>
创建快捷方式需要用到
需要jshortcut.jar和jshortcut.dll,当在eclipse里面运行的时候jshortcut.dll放在src根目录下面即口,当需要打包为jar的是时候jshortcut.dll应该放在跟jar同级目录下面