功能描述
1、webuploader是百度研发的上传组件,文档不是特别规整,但是也够用了。
2、前端使用官网的上传图片demo,在此基础上代码略微调整做分片。既可以上传图片也可以上传文件。文件超过分片大小才启用分片。
3、分片上传已做md5校验,达到秒传的效果。分片以后需要合并,可以先分片后合并,也可以边分片边合并,本示例采用的是边分片边合并的方案。
4、后端用springboot做框架搭建。springMVC做rest服务,开启跨域访问。
5、容器用springboot内置的tomcat插件,运行Application的main方法即可启动服务;
显示效果
关键代码
前端
[javascript] view plain copy
- WebUploader.Uploader.register({
- 'name': 'webUploaderHookCommand',
- 'before-send-file': 'beforeSendFile',
- "before-send": "beforeSend"
- }, {
- beforeSendFile: function(file) {
- var task = new WebUploader.Deferred();
- fileName = file.name;
- fileSize = file.size;
- (new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024).progress(function(percentage) {}).then(function(val) {
- fileMd5 = val;
- var url = checkUrl;
- var data = {
- type: 0,
- fileName: fileName,
- fileMd5: fileMd5,
- fileSize: fileSize
- };
- $.ajax({
- type: "POST",
- url: url,
- data: data,
- cache: false,
- async: false, // 同步
- timeout: 1000, // todo 超时的话,只能认为该分片未上传过
- dataType: "json",
- error: function(XMLHttpRequest, textStatus, errorThrown) {
- file.statusText = 'server_error';
- task.reject();
- }
- }).then(function(data, textStatus, jqXHR) {
- if(data.rtn == 0) {
- if(data.obj == 1) {
- file.statusText = 'file_existed';
- task.reject();
- } else {
- task.resolve();
- }
- } else {
- task.reject();
- }
- });
- });
- return task.promise();
- },
- beforeSend: function(block) {
- var task = new WebUploader.Deferred();
- var url = checkUrl;
- var data = {
- type: 1,
- fileName: fileName,
- fileMd5: fileMd5,
- chunk: block.chunk,
- fileSize: block.end - block.start
- };
- $.ajax({
- type: "POST",
- url: url,
- data: data,
- cache: false,
- async: false, // 同步
- timeout: 1000, // todo 超时的话,只能认为该分片未上传过
- dataType: "json"
- }).then(function(data, textStatus, jqXHR) {
- if(data.rtn == 0 && data.obj == 1) {
- task.reject(); // 分片存在,则跳过上传
- } else {
- task.resolve();
- }
- });
- this.owner.options.formData.fileMd5 = fileMd5;
- this.owner.options.formData.chunkSize = chunkSize;
- return task.promise();
- }
- });
- // 实例化
- uploader = WebUploader.create({
- pick: {
- id: '#filePicker',
- label: '点击选择文件'
- },
- formData: {
- uid: 123
- },
- dnd: '#dndArea', //指定文件拖拽的区域
- paste: '#uploader', //指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为document.body.
- swf: '../plugins/webuploader/Uploader.swf',
- chunked: true,
- chunkSize: chunkSize,
- chunkRetry: false,
- threads: 1,
- server: uploadUrl,
- // runtimeOrder: 'flash',
- // accept: {
- // title: 'Images',
- // extensions: 'gif,jpg,jpeg,bmp,png',
- // mimeTypes: 'image/*'
- // },
- // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
- disableGlobalDnd: true,
- fileNumLimit: 300 //限制多文件上传的个数
- //fileSizeLimit: 200 * 1024 * 1024, // 限制所有文件的大小 200 M
- //fileSingleSizeLimit: 50 * 1024 * 1024 // 限制单个文件的大小 50 M
- });
后端
[java] view plain copy
- import java.io.File;
- import java.io.IOException;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
- import org.springframework.web.multipart.MultipartFile;
- import com.bear.upload.util.FileUtil;
- import com.bear.upload.util.RETURN;
- import com.bear.upload.vo.CheckMd5FileVO;
- import com.bear.upload.vo.UploadVO;
- @Service
- public class ChunkUploadService {
- private static Logger LOG = LoggerFactory.getLogger(ChunkUploadService.class);
- @Value("${file.upload.path}")
- private String UPLOAD_PATH;
- private static final String Delimiter = "-";
- /**
- * 上传之前校验(整个文件、分片)
- *
- * @param md5FileVO
- * @return
- */
- public Object check(CheckMd5FileVO md5FileVO) {
- Integer type = md5FileVO.getType();
- Long chunk = md5FileVO.getChunk();
- String fileName = md5FileVO.getFileName();
- Long fileSize = md5FileVO.getFileSize();
- if (type == 0) {// 未分片校验
- String destfilePath = UPLOAD_PATH + File.separator + fileName;
- File destFile = new File(destfilePath);
- if (destFile.exists() && destFile.length() == fileSize) {
- return RETURN.success("文件已存在,跳过", 1);
- } else {
- return RETURN.success("文件不存在", 0);
- }
- } else {// 分片校验
- String fileMd5 = md5FileVO.getFileMd5();
- String destFileDir = UPLOAD_PATH + File.separator + fileMd5;
- String destFileName = chunk + Delimiter + fileName;
- String destFilePath = destFileDir + File.separator + destFileName;
- File destFile = new File(destFilePath);
- if (destFile.exists() && destFile.length() == fileSize) {
- return RETURN.success("分片已存在,跳过", 1);
- } else {
- return RETURN.success("分片不存在", 0);
- }
- }
- }
- /**
- * 文件上传
- *
- * @param file
- * @param uploadVO
- * @param appVersion
- * @return
- */
- public Object upload(MultipartFile file, UploadVO uploadVO) {
- Long chunk = uploadVO.getChunk();
- if (chunk == null) {// 没有分片
- return UnChunkUpload(file, uploadVO);
- } else {// 分片
- return ChunkUpload(file, uploadVO);
- }
- }
- /**
- * 分片上传
- *
- * @param file
- * @param uploadVO
- * @param appVersion
- * @return
- */
- public Object ChunkUpload(MultipartFile file, UploadVO uploadVO) {
- String fileName = uploadVO.getName();
- String fileMd5 = uploadVO.getFileMd5();
- Long chunk = uploadVO.getChunk();// 当前片
- Long chunks = uploadVO.getChunks();// 总共多少片
- // 分片目录创建
- String chunkDirPath = UPLOAD_PATH + File.separator + fileMd5;
- File chunkDir = new File(chunkDirPath);
- if (!chunkDir.exists()) {
- chunkDir.mkdirs();
- }
- // 分片文件上传
- String chunkFileName = chunk + Delimiter + fileName;
- String chunkFilePath = chunkDir + File.separator + chunkFileName;
- File chunkFile = new File(chunkFilePath);
- try {
- file.transferTo(chunkFile);
- } catch (Exception e) {
- LOG.error("分片上传出错", e);
- return RETURN.fail("分片上传出错", 1);
- }
- // 合并分片
- Long chunkSize = uploadVO.getChunkSize();
- long seek = chunkSize * chunk;
- String destFilePath = UPLOAD_PATH + File.separator + fileName;
- File destFile = new File(destFilePath);
- if (chunkFile.length() > 0) {
- try {
- FileUtil.randomAccessFile(chunkFile, destFile, seek);
- } catch (IOException e) {
- LOG.error("分片{}合并失败:{}", chunkFile.getName(), e.getMessage());
- return RETURN.fail("分片合并失败", 1);
- }
- }
- if (chunk == chunks - 1) {
- // 删除分片文件夹
- FileUtil.deleteDirectory(chunkDirPath);
- return RETURN.success("上传成功", 1);
- } else {
- return RETURN.fail("上传中...", 1);
- }
- }
- /**
- * 未分片上传
- *
- * @param file
- * @param uploadVO
- * @param appVersion
- * @return
- */
- public Object UnChunkUpload(MultipartFile file, UploadVO uploadVO) {
- String fileName = uploadVO.getName();
- // String fileMd5 = uploadVO.getFileMd5();
- // 文件上传
- File destFile = new File(UPLOAD_PATH + File.separator + fileName);
- if (file != null && !file.isEmpty()) {
- // 上传目录
- File fileDir = new File(UPLOAD_PATH);
- if (!fileDir.exists()) {
- fileDir.mkdirs();
- }
- if (destFile.exists()) {
- destFile.delete();
- }
- try {
- file.transferTo(destFile);
- return RETURN.success("上传成功", 0);
- } catch (Exception e) {
- LOG.error("文件上传出错", e);
- return RETURN.fail("文件上传出错", 0);
- }
- }
- return RETURN.fail("上传失败", 0);
- }
- }