java实现大文件分片上传

在项目中用到了大文件上传功能,最初从网上参考了一些代码来实现,但是最终的上传效果不是很好,速度比较慢。

之前的上传思路是:

  1. 前端利用webUploader分片大文件
  2. 后端接收各个分片后的小文件
  3. 接收完一个大文件的所有分片文件后,合并这些文件为大文件

现在的思路是:

  1. 前端利用webUploader分片大文件

  2. 后端接收各个分片后的小文件

  3. 将接收到的小文件直接存到目标文件夹

  4. 需要注意的是,一定要按分片顺序来存储小文件,不然最后生成的

    文件和原文件不一样,会被损坏

代码实现:

后端:

MediaUploadInfoController.java

/*** Copyright &copy; 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.*/
package com.thinkgem.jeesite.modules.uploadfiles.web;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.thinkgem.jeesite.common.response.Code;
import com.thinkgem.jeesite.common.response.Result;
import com.thinkgem.jeesite.common.utils.IdGen;
import com.thinkgem.jeesite.modules.mediafileinfo.entity.MediaFileInfo;
import com.thinkgem.jeesite.modules.mediafileinfo.service.MediaFileInfoService;
import com.thinkgem.jeesite.modules.sys.entity.Company;
import com.thinkgem.jeesite.modules.sys.entity.MediaType;
import com.thinkgem.jeesite.modules.sys.entity.User;
import com.thinkgem.jeesite.modules.sys.service.MediaSysCompanyService;
import com.thinkgem.jeesite.modules.sys.service.TypeService;
import com.thinkgem.jeesite.modules.sys.utils.UserUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;import com.thinkgem.jeesite.common.config.Global;
import com.thinkgem.jeesite.common.persistence.Page;
import com.thinkgem.jeesite.common.web.BaseController;
import com.thinkgem.jeesite.common.utils.StringUtils;
import com.thinkgem.jeesite.modules.uploadfiles.entity.MediaUploadInfo;
import com.thinkgem.jeesite.modules.uploadfiles.service.MediaUploadInfoService;import java.util.Arrays;
import java.util.List;
import java.util.Map;/*** 上传文件信息Controller* @author admin* @version 2018-07-11*/
@Controller
@RequestMapping(value = "${adminPath}/uploadfiles/mediaUploadInfo")
public class MediaUploadInfoController extends BaseController {@Autowiredprivate MediaUploadInfoService mediaUploadInfoService;@Autowiredprivate MediaSysCompanyService mediaSysCompanyService;@Autowiredprivate TypeService typeService;@Autowiredprivate MediaFileInfoService mediaFileInfoService;@ModelAttributepublic MediaUploadInfo get(@RequestParam(required=false) String id) {MediaUploadInfo entity = null;if (StringUtils.isNotBlank(id) && !StringUtils.contains(id, "WU_FILE")){entity = mediaUploadInfoService.get(id);}if (entity == null){entity = new MediaUploadInfo();}return entity;}// @RequiresPermissions("uploadfiles:mediaUploadInfo:view")@RequestMapping(value = {"list", ""})public String list(MediaUploadInfo mediaUploadInfo, HttpServletRequest request, HttpServletResponse response, Model model) {//todo 获得用户User user = UserUtils.getUser();mediaUploadInfo.setCreateBy(user);Page<MediaUploadInfo> page = mediaUploadInfoService.findPage(new Page<MediaUploadInfo>(request, response), mediaUploadInfo);model.addAttribute("page", page);return "modules/uploadfiles/mediaUploadInfoList";}// @RequiresPermissions("uploadfiles:mediaUploadInfo:view")@RequestMapping(value = "form")public String form(MediaUploadInfo mediaUploadInfo, Model model, @RequestParam(required = false) String rootId) {User user = UserUtils.getUser();String companyId = user.getCompany().getId();Company company = mediaSysCompanyService.get(Integer.valueOf(companyId));List<MediaType> typeList = typeService.getLastLevelTypes(rootId);model.addAttribute("UUID", IdGen.uuid());model.addAttribute("typeList", typeList);model.addAttribute("company", company);model.addAttribute("mediaUploadInfo", mediaUploadInfo);if ("1".equals(rootId))return "modules/uploadfiles/mediaUploadInfoForm";elsereturn "modules/uploadfiles/vedioUploadInfoForm";}@RequiresPermissions("uploadfiles:mediaUploadInfo:edit")@RequestMapping(value = "save")public String save(MediaUploadInfo mediaUploadInfo, Model model, RedirectAttributes redirectAttributes) {if (!beanValidator(model, mediaUploadInfo)){return form(mediaUploadInfo, model, null);}mediaUploadInfoService.save(mediaUploadInfo);addMessage(redirectAttributes, "保存文件成功");return "redirect:"+Global.getAdminPath()+"/uploadfiles/mediaUploadInfo/?repage";}@RequiresPermissions("uploadfiles:mediaUploadInfo:edit")@RequestMapping(value = "delete")public String delete(MediaUploadInfo mediaUploadInfo, RedirectAttributes redirectAttributes) {mediaUploadInfoService.delete(mediaUploadInfo);addMessage(redirectAttributes, "删除文件成功");return "redirect:"+Global.getAdminPath()+"/uploadfiles/mediaUploadInfo/?repage";}/*** 分片上传文件** @param mediaUploadInfo MediaUploadInfo实体* @param mediaFileInfo MediaFileInfo实体* @param uploadCounts 上传的文件总数量* @param UUID 作为media_upload_info表中数据主键* @param isCover 是否是封面,图片上传时用于判定封面图片* @param isVideo 是否是视频文件* @param chunks 文件总的分片数量* @param chunk 当前分片* @param name 名字* @param request 请求* @param file 文件* @param md5Value 文件MD5值,用于多文件上传时,判定是不是相同文件* @return json信息*/@RequestMapping("/sliceUploadFiles")@ResponseBodypublic Map<String, Object> sliceUploadFiles(MediaUploadInfo mediaUploadInfo,MediaFileInfo mediaFileInfo,int uploadCounts,String UUID,Boolean isCover,Boolean isVideo,String chunks,String chunk,String name,HttpServletRequest request,MultipartFile file,String md5Value) {try {return mediaUploadInfoService.sliceUploadFiles(mediaUploadInfo, mediaFileInfo, uploadCounts, UUID, isCover, isVideo, chunks, chunk, name, request, file, md5Value);} catch (Exception e) {return Result.Error(Code.SYSTEM_ERROR);}}/*** 视频上传封面上传** @param cover* @param UUID* @param mediaUploadInfo* @return*/@RequestMapping("/coverUpload")@ResponseBodypublic Map<String, Object> coverUpload(HttpServletRequest request, MultipartFile file, String UUID, MediaUploadInfo mediaUploadInfo, String md5Value) {return mediaUploadInfoService.coverUpload(request, file, UUID, mediaUploadInfo, md5Value);}/*** 批量删除文件** @param ids 多个文件ID,用“,”隔开* @return*/@RequestMapping("/batchDelete")@ResponseBody@Transactionalpublic Map<String, Object> batchDelete(String ids){String[] strings = ids != null ? ids.split(",") : null;if (strings != null && strings.length > 0){List<String> pkIds = Arrays.asList(strings);//批量删除可以被删除的文件信息,审核通过的不能删除Integer count = mediaUploadInfoService.batchDelete(pkIds);Integer count1 = mediaFileInfoService.batchDelete(pkIds);if (count == 0 && count1 == 0){return Result.Success("您选择的文件因为已经审核通过,不能删除!");}else {return Result.Success("您选择的文件删除成功!");}}else {return Result.Error(Code.PARAM_ERROR);}}}

MediaUploadInfoService.java

/*** Copyright &copy; 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.*/
package com.thinkgem.jeesite.modules.uploadfiles.service;import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.thinkgem.jeesite.common.config.Global;
import com.thinkgem.jeesite.common.persistence.Page;
import com.thinkgem.jeesite.common.response.Code;
import com.thinkgem.jeesite.common.response.Result;
import com.thinkgem.jeesite.common.service.CrudService;
import com.thinkgem.jeesite.common.utils.FileUtils;
import com.thinkgem.jeesite.common.utils.IdGen;
import com.thinkgem.jeesite.common.utils.UploadFiles.OperatingFileUtil;
import com.thinkgem.jeesite.modules.mediafileinfo.entity.MediaFileInfo;
import com.thinkgem.jeesite.modules.mediafileinfo.service.MediaFileInfoService;
import com.thinkgem.jeesite.modules.sys.entity.MediaType;
import com.thinkgem.jeesite.modules.sys.entity.User;
import com.thinkgem.jeesite.modules.sys.service.TypeService;
import com.thinkgem.jeesite.modules.sys.utils.UserUtils;
import com.thinkgem.jeesite.modules.uploadfiles.dao.MediaUploadInfoDao;
import com.thinkgem.jeesite.modules.uploadfiles.entity.MediaUploadInfo;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/*** 上传文件信息Service* @author admin* @version 2018-07-11*/
@Service
@Transactional(readOnly = false)
public class MediaUploadInfoService extends CrudService<MediaUploadInfoDao, MediaUploadInfo> {//上传文件根目录private static final String BASEDIR = Global.getConfig("uploadFile.baseDir");private static final String TEMPDIR = "temp";private static final String DEST_PATH = "destPath";private static final String SAVE_PATH = "savePath";private static final String MEDIA_TYPE = "media_type";/*** 线程安全Map,存放media_file_type表内容,不同时候上传,从数据库读取到的该表数据有变化,* 所以以各次上传读取到的数据为准,即使在上传过程中数据有变化,key组成:media_type_${UUID}_${md5Value},* 其中UUID为各次上传的UUID,md5Value代表每个文件的md5值*/private static Map<String, List<MediaType>> MEDIA_TYPE_MAP = new ConcurrentHashMap<>();/*** 线程安全Map,key存放media_file_type对应id(即文件类型ID) + _ + ${UUID} + _ + ${md5Value},其中UUID为各次上传的UUID,* 如果 MEDIA_TYPE_MAP 中的value变化,则该key的value也相应变化,所以用UUID来标识* value存放从树根节点一直到该文件类型ID对应节点所经过的所有节点,用来组成存放文件的路径*/private static Map<String, Object> FATHER_AND_SON_MAP = new ConcurrentHashMap<>();/*** 线程安全Map,key组成:destPath/savePath_${UUID}_${md5Value},其中UUID为各次上传的UUID,每次上传的目标路径不同*/private static Map<String, Object> PATH_MAP = new ConcurrentHashMap<>();/*** 上传个数计数器,key代表一次上传的UUID,value是这次上传的文件个数计数器*/private static Map<String, AtomicInteger> uploadCountMap = new ConcurrentHashMap<>();private static Map<String, AtomicInteger> chunkCountMap = new ConcurrentHashMap<>();//上传封面图片长private static final int hight = 350;//上传封面图片宽private static final int width = 500;@Autowiredprivate TypeService typeService;public MediaUploadInfo get(String id) {return super.get(id);}public List<MediaUploadInfo> findList(MediaUploadInfo mediaUploadInfo) {return super.findList(mediaUploadInfo);}public Page<MediaUploadInfo> findPage(Page<MediaUploadInfo> page, MediaUploadInfo mediaUploadInfo) {return super.findPage(page, mediaUploadInfo);}@Transactional(readOnly = false)public void save(MediaUploadInfo mediaUploadInfo) {super.save(mediaUploadInfo);}@Transactional(readOnly = false)public void delete(MediaUploadInfo mediaUploadInfo) {super.delete(mediaUploadInfo);}@Autowiredprivate MediaFileInfoService mediaFileInfoService;/*** 分片上传文件** @param mediaUploadInfo MediaUploadInfo实体* @param mediaFileInfo MediaFileInfo实体* @param uploadCounts 该批次上传的文件总数量* @param UUID 作为media_upload_info表中数据主键* @param isCover 是否是封面,图片上传时用于判定封面图片* @param isVideo 是否是视频文件* @param chunks 文件总的分片数量* @param chunk 当前分片* @param name 名字* @param request 请求* @param file 文件* @param md5Value 文件MD5值,用于多文件上传时,判定是不是相同文件* @return json信息*/public Map<String,Object> sliceUploadFiles(MediaUploadInfo mediaUploadInfo,MediaFileInfo mediaFileInfo,int uploadCounts,String UUID,Boolean isCover,Boolean isVideo,String chunks,String chunk,String name,HttpServletRequest request,MultipartFile file, String md5Value) {//目标文件夹String destPath, savePath;//文件扩展名,包括".",例如".mp4"String ext = name.substring(name.lastIndexOf("."));//文件真实扩展名,不包括"."String realExt = ext.substring(ext.lastIndexOf(".") + 1);//工程根目录,最后不包括分隔符String realPath = OperatingFileUtil.getProjectPath(request);//文件最终保存名字前缀,文件最终保存名字String prefixNewName,newName;//父类型list,用于生成保存文件路径List<String> parentTypeList;//获取日期,用于目标文件夹目录创建Date date = new Date();SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");String today = format.format(date);if (PATH_MAP.get(Joiner.on("_").join(DEST_PATH, UUID, md5Value)) == null|| PATH_MAP.get(Joiner.on("_").join(SAVE_PATH, UUID, md5Value)) == null|| FATHER_AND_SON_MAP.get(Joiner.on("_").join(mediaUploadInfo.getUploadType(), UUID, md5Value)) == null){logger.debug(Thread.currentThread().getName() + "开始计算路径");//获取该节点所有父类型,用于目标文件夹目录创建parentTypeList = this.getParentTypeList(mediaUploadInfo, UUID, md5Value);try {destPath = this.getLinuxFilePath(realPath+BASEDIR, today, parentTypeList, mediaUploadInfo.getUploadType(), UUID);//开头不包括"/"savePath = this.getLinuxFilePath(BASEDIR, today, parentTypeList, mediaUploadInfo.getUploadType(), UUID).substring(1);}catch (NullPointerException | ClassNotFoundException e){logger.error("拼接上传文件路径异常!", e);return Result.Error(Code.SYSTEM_ERROR);}PATH_MAP.putIfAbsent(Joiner.on("_").join(DEST_PATH, UUID, md5Value), destPath);PATH_MAP.putIfAbsent(Joiner.on("_").join(SAVE_PATH, UUID, md5Value), savePath);logger.debug(Thread.currentThread().getName() + "结束计算路径");}else {logger.debug(Thread.currentThread().getName() + "开始获取路径");destPath = (String) PATH_MAP.get(Joiner.on("_").join(DEST_PATH, UUID, md5Value));savePath = (String) PATH_MAP.get(Joiner.on("_").join(SAVE_PATH, UUID, md5Value));parentTypeList = (List<String>) FATHER_AND_SON_MAP.get(Joiner.on("_").join(mediaUploadInfo.getUploadType(), UUID, md5Value));logger.debug(Thread.currentThread().getName() + "结束获取路径");}//合并后文件的名字前缀prefixNewName = md5Value+UUID;//合并后文件的名字newName = prefixNewName + ext;int chunkUploadCount;// 将文件分片保存到文件夹里,按分片顺序保存,轮询到自己的顺序再保存while (true){logger.debug(Thread.currentThread().getName() + "轮询开始");if (chunkCountMap.get(Joiner.on("_").join(UUID, md5Value)) == null){logger.debug(Thread.currentThread().getName() + "给chunkCountMap设置初始值开始");AtomicInteger uploadCount = new AtomicInteger(0);chunkCountMap.putIfAbsent(Joiner.on("_").join(UUID, md5Value), uploadCount);logger.debug(Thread.currentThread().getName() + "给chunkCountMap设置初始值结束");//如果chunkCountMap里面该文件已经上传的个数,与该分片相差1,则轮到该分片存储}else if (Integer.valueOf(chunk) - chunkCountMap.get(Joiner.on("_").join(UUID, md5Value)).intValue() + 1 == 1){logger.debug(Thread.currentThread().getName() + "开始保存文件" + "chunk = " + chunk);if(!OperatingFileUtil.saveFile(destPath, newName, file)){return Result.Success("分片文件保存失败!");}// 分片上传成功总数增加1chunkUploadCount = OperatingFileUtil.incrAndGet(Joiner.on("_").join(UUID, md5Value), chunkCountMap);logger.debug(Thread.currentThread().getName() + "保存文件结束" + "chunk = " + chunk);break;}}//如果所有分片没有上传完成,则直接返回if (!OperatingFileUtil.isAllUploaded(chunkUploadCount, chunks, md5Value, uploadCountMap)){return Result.Success("上传成功!");}//等待所有分片文件上传完成,存储文件信息try {//计数器计数int countAfterIncr = OperatingFileUtil.incrAndGet(UUID, uploadCountMap);//封面进行压缩后保存的文件名String coverName = IdGen.uuid() + ext;//查询该批次是否有上传信息MediaUploadInfo info = this.get(UUID);//说明是这个批次不是第一次上传,只有视频的封面存在这种情况if (info != null){if (isCover){//删除原来压缩的封面图片String filePath = this.getLinuxFilePath(realPath, "/", info.getUploadCover());FileUtils.deleteFile(filePath);//删除数据库中保存的封面信息List<String> paths = Splitter.on("/").splitToList(info.getUploadCover());String fileName = paths.get(paths.size() - 1);String id = fileName.substring(0, fileName.indexOf("."));MediaFileInfo mediaFileInfo1 = new MediaFileInfo();mediaFileInfo1.setDelFlag(1);mediaFileInfo1.setFileId(id);mediaFileInfoService.delete(mediaFileInfo1);}//用来更新infomediaUploadInfo.setUploadId(info.getUploadId());}//如果是封面if (isCover) {boolean state;//封面进行压缩,如果是视频封面不保存原文件,直接压缩覆盖原文件,如果是图片封面不能覆盖if (isVideo){state = this.reduceImg(destPath + newName, destPath + newName, width, hight, null);mediaUploadInfo.setUploadCover(savePath + newName);}else{state = this.reduceImg(destPath + newName, destPath + coverName, width, hight, null);mediaUploadInfo.setUploadCover(savePath + coverName);}if (!state){//还原uploadCountMap里面上传次数uploadCountMap.get(UUID).decrementAndGet();Code.SYSTEM_ERROR.setMes("上传失败!");return Result.Error(Code.SYSTEM_ERROR);}mediaUploadInfo.setUploadParentType(parentTypeList.get(parentTypeList.size()-1));this.saveFileInfo(UUID, mediaUploadInfo);} else { //对于视频走下面逻辑if (countAfterIncr == uploadCounts){mediaUploadInfo.setUploadParentType(parentTypeList.get(parentTypeList.size()-1));this.saveFileInfo(UUID, mediaUploadInfo);}}//保存单个文件信息mediaFileInfo.setFileId(prefixNewName);mediaFileInfo.setFileName(name);mediaFileInfo.setFileSuffix(realExt);mediaFileInfo.setFileAttach(UUID);mediaFileInfo.setFileUrl(savePath + newName);mediaFileInfo.setFileNum(String.valueOf(System.currentTimeMillis()));mediaFileInfo.setFileSize(String.valueOf(getFileSize(destPath + newName) / 1024)); //单位KBmediaFileInfo.setIsNewRecord(true);mediaFileInfoService.save(mediaFileInfo);//清除缓存的信息if (countAfterIncr == uploadCounts){uploadCountMap.remove(UUID);MEDIA_TYPE_MAP.remove(Joiner.on("_").join(MEDIA_TYPE, UUID, md5Value));FATHER_AND_SON_MAP.remove(Joiner.on("_").join(mediaUploadInfo.getUploadType(), UUID, md5Value));PATH_MAP.remove(Joiner.on("_").join(DEST_PATH, UUID, md5Value));PATH_MAP.remove(Joiner.on("_").join(SAVE_PATH, UUID, md5Value));}} catch (SQLException e) {logger.error("MediaUploadInfoService类sliceUploadFile方法保存文件信息异常!", e);return Result.Error(Code.SYSTEM_ERROR);} catch (ClassNotFoundException e) {logger.error("拼接上传文件路径异常!", e);return Result.Error(Code.SYSTEM_ERROR);}catch (Exception e){logger.error("系统异常!", e);return Result.Error(Code.SYSTEM_ERROR);}return Result.Success("上传成功!");}/*** 获得文件大小* @param file* @return*/private long getFileSize(File file){if (file.isFile()){return file.length();}return 0L;}/*** 通过文件名获得文件大小* @param fileName 文件绝对路径* @return 文件大小*/private long getFileSize(String fileName){File file = new File(fileName);return getFileSize(file);}/*** 计数器进行计数并且返回增加后的值** @param key ConcurrentHashMap中的key值* @return 增加后的值*/private int incrAndGet(String key){int countAfterIncr;if (uploadCountMap.get(key) == null){AtomicInteger uploadCount = new AtomicInteger(0);/*** putIfAbsent方法可以不覆盖原来key对应的value,* 如果key对应的value不存在(新的entry),那么会向uploadCountMap中添加该键值对,并返回null,* 如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值,* 即使刚开始两个线程同时进来,也不会导致两个线程互相覆盖* 所以多个线程执行这句代码都相当于执行了一次*/uploadCountMap.putIfAbsent(key, uploadCount);}countAfterIncr = uploadCountMap.get(key).incrementAndGet();return countAfterIncr;}/*** 得到某个类型的所有父类型list** @param mediaUploadInfo* @param UUID* @param md5Value* @return*/private List<String> getParentTypeList(MediaUploadInfo mediaUploadInfo, String UUID, String md5Value){List<MediaType> list;if (MEDIA_TYPE_MAP.get(Joiner.on("_").join(MEDIA_TYPE, UUID, md5Value)) == null){
//       logger.debug(Thread.currentThread().getName() + "开始计算MEDIA_TYPE_MAP");list = typeService.findAllListFront();MEDIA_TYPE_MAP.putIfAbsent(Joiner.on("_").join(MEDIA_TYPE, UUID, md5Value), list);
//       logger.debug(Thread.currentThread().getName() + "结束计算MEDIA_TYPE_MAP");}else {
//       logger.debug(Thread.currentThread().getName() + "开始获取MEDIA_TYPE_MAP里面的list");list = MEDIA_TYPE_MAP.get(Joiner.on("_").join(MEDIA_TYPE, UUID, md5Value));
//       logger.debug(Thread.currentThread().getName() + "结束获取MEDIA_TYPE_MAP里面的list");}StringBuffer pids = new StringBuffer();//父类型id的listList<String> parentTypeList = new ArrayList<>();if (!FATHER_AND_SON_MAP.containsKey(Joiner.on("_").join(mediaUploadInfo.getUploadType(), UUID, md5Value))){
//       logger.debug(Thread.currentThread().getName() + "开始计算FATHER_AND_SON_MAP");MediaType mediaType = typeService.getType(mediaUploadInfo.getUploadType());typeService.findPids(list, mediaType, pids);String[] strs = StringUtils.isNotBlank(pids.toString()) ? pids.toString().split(",") : null;if (strs != null){parentTypeList = Arrays.asList(strs);}FATHER_AND_SON_MAP.putIfAbsent(Joiner.on("_").join(mediaUploadInfo.getUploadType(), UUID, md5Value), parentTypeList);
//       logger.debug(Thread.currentThread().getName() + "结束计算FATHER_AND_SON_MAP");}else {
//       logger.debug(Thread.currentThread().getName() + "开始获取FATHER_AND_SON_MAP里面parentTypeList");parentTypeList = (List<String>) FATHER_AND_SON_MAP.get(Joiner.on("_").join(mediaUploadInfo.getUploadType(), UUID, md5Value));
//       logger.debug(Thread.currentThread().getName() + "结束获取FATHER_AND_SON_MAP里面parentTypeList");}return parentTypeList;}/*** 拼接Linux下文件路径** @param path 多个表示路径的字符串,第一个字符串首字母可以是"/",或者是单纯字符串,不带"/"* @return 拼接成的文件路径*/@SuppressWarnings("unchecked")private String getLinuxFilePath(Object... path) throws ClassNotFoundException {if (path == null || path.length == 0) {throw new NullPointerException("path 不能为空!");}
//    StringBuilder destPath = new StringBuilder("/");StringBuilder destPath = new StringBuilder();for (Object item : path) {if (item instanceof String){if (StringUtils.isBlank(item.toString())){throw new NullPointerException("path 不能为空或者空字符串!");}destPath.append(item.toString()).append("/");}else if (item instanceof List<?>){for (String s : (List<String>)item) {if (StringUtils.isBlank(s)){throw new NullPointerException("path 不能为空或者空字符串!");}destPath.append(s).append("/");}}elsethrow new ClassNotFoundException("该方法只支持String或者String集合的参数!");}String result = destPath.toString();return result.charAt(1) != "/".charAt(0) ? result : result.substring(1);}/*** 保存信息到media_upload_info表** @param UUID 主键* @param mediaUploadInfo 实体* @throws SQLException*/private void saveFileInfo(String UUID, MediaUploadInfo mediaUploadInfo) throws SQLException {//未审核mediaUploadInfo.setToExamineStatus("0");//允许下载mediaUploadInfo.setIsDownload("0");User user = UserUtils.getUser();mediaUploadInfo.setSubjectionCompany(user.getCompany().getId());if (mediaUploadInfo.getUploadId() == null){mediaUploadInfo.setUploadId(UUID);mediaUploadInfo.setIsNewRecord(true);this.save(mediaUploadInfo);}elsethis.update(mediaUploadInfo);}/*** 通过主键获得能够被删除的文件信息** @param pkIds 主键集合* @return*/public List<MediaUploadInfo> findCanDelete(List<String> pkIds) {return dao.findCanDelete(pkIds);}/*** 批量删除能被删除的文件信息** @param pkIds 主键集合*/public Integer batchDelete(List<String> pkIds) {return dao.batchDelete(pkIds);}/*** 视频上传中上传封面图片** @param file 文件* @param UUID 主键* @param mediaUploadInfo 实体类,主要包含封面图片存储地址* @return json信息* @deprecated*/public Map<String,Object> coverUpload(HttpServletRequest request, MultipartFile file, String UUID, MediaUploadInfo mediaUploadInfo, String md5Value) {//目标文件夹String destPath, savePath;Date date = new Date();SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");//获取日期,用于目标文件夹目录创建String today = format.format(date);List<String> parentTypeList = this.getParentTypeList(mediaUploadInfo, UUID, md5Value);//工程根目录,不包括最后一个分隔符String realPath = OperatingFileUtil.getProjectPath(request);try {destPath = this.getLinuxFilePath(realPath+BASEDIR, today, parentTypeList, mediaUploadInfo.getUploadType(), UUID);//存相对路径savePath = this.getLinuxFilePath(BASEDIR, today, parentTypeList, mediaUploadInfo.getUploadType(), UUID).substring(1);}catch (NullPointerException | ClassNotFoundException e){logger.error("MediaUploadInfoService类sliceUploadFile方法执行异常!", e);return Result.Error(Code.SYSTEM_ERROR);}String name = file.getOriginalFilename();//文件扩展名,包括".",例如".mp4"String ext = name.substring(name.lastIndexOf("."));//文件真实扩展名,不包括"."String realExt = ext.substring(ext.lastIndexOf(".") + 1);//文件最终保存名字前缀String prefixNewName = IdGen.uuid();//文件最终保存名字String newName = prefixNewName + ext;//保存文件到目标文件夹if(!OperatingFileUtil.saveFile(destPath, newName, file)){Code.SYSTEM_ERROR.setMes("上传失败!请重新上传");return Result.Error(Code.SYSTEM_ERROR);}try {//查询该批次是否有上传信息MediaUploadInfo info = this.get(UUID);//说明是这个批次不是第一次上传if (info != null){//删除原来压缩的封面图片String filePath = this.getLinuxFilePath(realPath, "/", info.getUploadCover());FileUtils.deleteFile(filePath);//用来更新infomediaUploadInfo.setUploadId(info.getUploadId());}//封面进行压缩boolean state = this.reduceImg(destPath + newName, destPath + newName, width, hight, null);if (!state){Code.SYSTEM_ERROR.setMes("上传失败!");return Result.Error(Code.SYSTEM_ERROR);}//更新文件信息mediaUploadInfo.setUploadCover(savePath + newName);this.saveFileInfo(UUID, mediaUploadInfo);}catch (SQLException e){logger.error("MediaUploadInfoService类coverUpload方法保存文件信息异常!", e);Code.SYSTEM_ERROR.setMes("封面图片保存数据库失败!");return Result.Error(Code.SYSTEM_ERROR);} catch (ClassNotFoundException e) {logger.error("拼接上传文件路径异常!", e);return Result.Error(Code.SYSTEM_ERROR);}return Result.Success("封面图片上传成功!");}/*** 指定图片宽度和高度和压缩比例对图片进行压缩** @param imgsrc 源图片地址* @param imgdist 目标图片地址* @param widthdist 压缩后图片的宽度* @param heightdist 压缩后图片的高度* @param rate 压缩的比例*/public boolean reduceImg(String imgsrc, String imgdist, int widthdist, int heightdist, Float rate) {try {File srcfile = new File(imgsrc);// 检查图片文件是否存在if (!srcfile.exists()) {System.out.println("文件不存在");}// 如果比例不为空则说明是按比例压缩if (rate != null && rate > 0) {//获得源图片的宽高存入数组中int[] results = getImgWidthHeight(srcfile);if (results == null || results[0] == 0 || results[1] == 0) {return false;} else {//按比例缩放或扩大图片大小,将浮点型转为整型widthdist = (int) (results[0] * rate);heightdist = (int) (results[1] * rate);}}// 开始读取文件并进行压缩Image src = ImageIO.read(srcfile);// 构造一个类型为预定义图像类型之一的 BufferedImageBufferedImage tag = new BufferedImage((int) widthdist, (int) heightdist, BufferedImage.TYPE_INT_RGB);//绘制图像  getScaledInstance表示创建此图像的缩放版本,返回一个新的缩放版本Image,按指定的width,height呈现图像//Image.SCALE_SMOOTH,选择图像平滑度比缩放速度具有更高优先级的图像缩放算法。tag.getGraphics().drawImage(src.getScaledInstance(widthdist, heightdist, Image.SCALE_SMOOTH), 0, 0, null);//创建文件输出流FileOutputStream out = new FileOutputStream(imgdist);//将图片按JPEG压缩,保存到out中
//       JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
//       encoder.encode(tag);String formatName = imgdist.substring(imgdist.lastIndexOf(".") + 1);ImageIO.write(tag, formatName, out);//关闭文件输出流out.close();} catch (Exception ef) {ef.printStackTrace();return false;}return true;}public static int[] getImgWidthHeight(File file) {InputStream is = null;BufferedImage src = null;int result[] = {0,0};try {// 获得文件输入流is = new FileInputStream(file);// 从流里将图片写入缓冲图片区src = ImageIO.read(is);result[0] =src.getWidth(null); // 得到源图片宽result[1] =src.getHeight(null);// 得到源图片高is.close();  //关闭输入流} catch (Exception ef) {ef.printStackTrace();}return result;}public static void main(String[] args) {
//    String uuid = IdGen.uuid();
//    System.out.println(uuid);
//    String srcPath = "C:\\nfs\\sources\\uploadFiles\\2018\\07\\30\\0\\1\\494b2b8ffead4eebb7f4d25991a7a66e\\5d35ab59efd0456abe85865f1c14061d\\d262e2bdb6c04ad4a28ad3b5494d6390.jpg";
//    String destPath = "C:\\nfs\\sources\\uploadFiles\\2018\\07\\30\\0\\1\\494b2b8ffead4eebb7f4d25991a7a66e\\5d35ab59efd0456abe85865f1c14061d\\1.jpg";
//    File srcfile = new File(srcPath);
//    File distfile = new File(destPath);
//
//    System.out.println("压缩前图片大小:" + srcfile.length());
//    reduceImg(srcPath, destPath, 500, 350, null);
//    System.out.println("压缩后图片大小:" + distfile.length());
//    for (int i = 0; i < 100; ++i){
//       Thread thread = new Thread(() -> System.out.println(incrAndGet("a")));
//       thread.start();
//    }}
}

OperatingFileUtil.java

package com.thinkgem.jeesite.common.utils.UploadFiles;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.channels.FileChannel;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.atomic.AtomicInteger;@Slf4j
public class OperatingFileUtil {//上传个数计数器,key代表一次上传的UUID,value是这次上传的文件个数计数器private static Map<String, AtomicInteger> uploadCountMap = new ConcurrentHashMap<>();/*** 取得tomcat中的webapps目录 如:/home/apache-tomcat-8.0.45/webapps* @param request 请求* @return 真实路径*/public static String getRealPath(HttpServletRequest request) {String realPath = request.getSession().getServletContext().getRealPath(File.separator);realPath = realPath.substring(0, realPath.length() - 1);int aString = realPath.lastIndexOf(File.separator);realPath = realPath.substring(0, aString);return realPath;}/*** 取得tomcat中的项目目录 如:/home/apache-tomcat-8.0.45/webapps/ysp* @param request 请求* @return 真实路径*/public static String getProjectPath(HttpServletRequest request) {String realPath = request.getSession().getServletContext().getRealPath(File.separator);int aString = realPath.lastIndexOf(File.separator);realPath = realPath.substring(0, aString);return realPath;}/*** 保存文件到指定路径** @param savePath 保存的路径* @param fileFullName 文件名字,包括扩展名* @param file 文件* @return true:保存成功,false:保存失败*/public static boolean saveFile(String savePath, String fileFullName, MultipartFile file) {// 判断文件夹是否存在,不存在就创建一个File fileDirectory = new File(savePath);if (!fileDirectory.exists()) {fileDirectory.mkdirs();}File uploadFile = new File(savePath + fileFullName);byte[] data = new byte[0];try {data = readInputStream(file.getInputStream());} catch (IOException e) {log.error("文件" + (savePath+fileFullName) +"读入失败", e);return false;}// 创建输出流try (FileOutputStream outStream = new FileOutputStream(uploadFile, true)) {// 写入数据outStream.write(data);outStream.flush();} catch (FileNotFoundException e) {log.error("文件" + (savePath+fileFullName) +"未找到", e);return false;} catch (IOException e) {log.error("写入数据异常,写入路径为:" + (savePath+fileFullName), e);return false;}return uploadFile.exists();}/*** 读取输入流到byte[]中** @param in 输入流* @return 读取到的byte数组* @throws IOException 异常*/private static byte[] readInputStream(InputStream in) throws IOException {ByteArrayOutputStream outStream = new ByteArrayOutputStream();// 创建一个Buffer字符串byte[] buffer = new byte[1024];// 每次读取的字符串长度,如果为-1,代表全部读取完毕int len;// 使用一个输入流从buffer里把数据读取出来while ((len = in.read(buffer)) != -1) {// 用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度outStream.write(buffer, 0, len);}// 关闭输入流in.close();// 把outStream里的数据写入内存return outStream.toByteArray();}/*** 计数器进行计数并且返回增加后的值** @param key ConcurrentHashMap中的key值* @return 增加后的值*/public static int incrAndGet(String key, Map<String, AtomicInteger> map){int countAfterIncr;if (map.get(key) == null){AtomicInteger uploadCount = new AtomicInteger(0);/*** putIfAbsent方法可以不覆盖原来key对应的value,* 如果key对应的value不存在(新的entry),那么会向uploadCountMap中添加该键值对,并返回null,* 如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值,* 即使刚开始两个线程同时进来,也不会导致两个线程互相覆盖* 所以多个线程执行这句代码都相当于执行了一次*/map.putIfAbsent(key, uploadCount);}countAfterIncr = map.get(key).incrementAndGet();return countAfterIncr;}/*** 是否全部上传完成** @param md5 MD5值* @param chunks 分片总数* @return true:一个文件所有分片上传成功,false:一个文件不是所有分片上传成功*/public static boolean isAllUploaded(int uploadCount, String chunks, String md5Value, Map<String, AtomicInteger> map) {if (uploadCount == Integer.parseInt(chunks)){map.remove(md5Value);return true;}elsereturn false;}/*** 把输入流保存到目标文件夹** @param inputStream 输入流* @param filePath 目标文件夹* @param newName 新名字* @return true:保存成功,false:保存失败*/public static boolean saveStreamToFile(SequenceInputStream inputStream, String filePath, String newName) {boolean result = true;File fileDirectory = new File(filePath);if (!fileDirectory.exists()) {if (!fileDirectory.mkdirs()) {log.error("保存文件的文件夹创建失败!路径为:[" + fileDirectory + "]");return false;}}/* 创建输出流,写入数据,合并分块 */byte[] buffer = new byte[1024];int len = 0;try (OutputStream outputStream = new FileOutputStream(filePath + newName)) {//Reads some number of bytes from the input stream and stores them into the buffer arraywhile ((len = inputStream.read(buffer)) != -1) {//Writes <code>len</code> bytes from the specified byte array starting at offset <code>off</code> to this output streamoutputStream.write(buffer, 0, len);//Flushes this output stream and forces any buffered output bytes to be written out, such bytes should immediately be written to their intended destinationoutputStream.flush();}}catch (FileNotFoundException e) {log.error("文件[" + (filePath+newName) + "]未找到!", e);result = false;}catch (IOException e) {log.error("文件["+(filePath + newName)+"]IOException", e);result = false;} finally {try {inputStream.close();} catch (IOException e) {log.error("OperatingFileUtil类#saveStreamToFile方法SequenceInputStream关闭异常", e);result = false;}}return result;}/*** 删除文件夹以及文件夹里面所有文件** @param filePath 文件夹路径* @return true:删除成功,false:删除失败*/public static boolean deleteFolder(String filePath) {File dir = new File(filePath);File[] files = dir.listFiles();if (files != null) {for (File file : files) {if (file.isDirectory()){deleteFolder(file.getPath());}else {if(!file.delete()) {log.info("文件{}删除失败", file.getName());return false;}}}}return dir.delete();}}

其余的代码是业务逻辑相关,不关乎实现

前端实现:
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<html>
<head><title>视频上传</title><meta name="decorator" content="default"/><link rel="stylesheet" type="text/css" href="${ctxStatic}/webuploader/webuploader.css"><script type="text/javascript" src="${ctxStatic}/webuploader/webuploader.min.js"></script><link rel="stylesheet" href="${ctxStatic}/jiaoben5515/css/zcity.css"><script type="text/javascript" src="${ctxStatic}/jiaoben5515/js/zcity.js"></script><style>.handleArea{position: absolute;top:0;left:0;width: 100px;height: 100px;background-color: rgba(0,0,0,.7);display: none;}.info:hover .handleArea{display: block;}.zcityGroup{float: left;width: 50%;}.zcityGroup .zcityItem{width: 48%;}</style>
</head>
<body><br/><form:form id="inputForm" modelAttribute="mediaUploadInfo" action="${ctx}/uploadfiles/mediaUploadInfo/save" method="post" class="form-horizontal m-b-55"><sys:message content="${message}"/>       <div class="control-group"><label class="control-label">文件属于&nbsp;${company.name}:</label><div class="controls" style="padding-top: 3px;"><input type="radio" id="share" name="shareStatus" value="0" checked/><label for="share" >共享</label> &nbsp;&nbsp; &nbsp; &nbsp;<input type="radio" id="nonShare" name="shareStatus" value="1" /><label for="nonShare">不共享</label> &nbsp;</div></div><div class="control-group"><label class="control-label">上传标题:</label><div class="controls"><form:input path="uploadTitle" htmlEscape="false" maxlength="255" class="input-xlarge required"/><span class="help-inline"><font color="red">*</font> </span></div></div><%--<div class="control-group">--%><%--<label class="control-label">标题封面物理路径:</label>--%><%--<div class="controls">--%><%--<form:input path="uploadCover" htmlEscape="false" maxlength="255" class="input-xlarge "/>--%><%--</div>--%><%--</div>--%><div class="control-group"><label class="control-label">上传分类:</label><div class="controls"><form:select path="uploadType" class="input-xlarge required" style="width: 284px"><form:options items="${typeList}" itemLabel="name" itemValue="id" htmlEscape="false"/></form:select><span class="help-inline"><font color="red">*</font> </span></div></div><div class="control-group"><label class="control-label">摄影师:</label><div class="controls"><form:input path="shootAuthor" htmlEscape="false" maxlength="255" class="input-xlarge "/></div></div><div class="control-group"><label class="control-label">拍摄时间:</label><div class="controls"><input id="shootTime" name="shootTime" type="text" readonly="readonly" maxlength="20" class="input-medium Wdate "value="<fmt:formatDate value="${mediaUploadInfo.shootTime}" pattern="yyyy-MM-dd HH:mm:ss"/>"onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',isShowClear:false});"/></div></div><div class="control-group"><label class="control-label">拍摄地点:</label><div class="controls"><div class="zcityGroup" city-range="{'level_start':1,'level_end':2}" city-ini="四川,成都市"></div><input type="text" id="shootplace" placeholder="输入拍摄地点" style="height: 21px"/></div></div><div class="control-group"><label class="control-label">描述、说明、备注:</label><div class="controls"><form:textarea path="remarks" htmlEscape="false" rows="4" class="input-xxlarge required"/><span class="help-inline"><font color="red">*</font> </span></div></div><div class="control-group"><label class="control-label">封面图片:</label><div class="controls"><div id="uploaderimg" class="wu-example"><!--用来存放文件信息--><div id="imglist" class="uploader-list o-h"></div><div class="btns"><div id="pickerimg" class="pull-left">选择图片<!-- <input type="file" class="webuploader-container"> --></div><!-- <button id="ctlBtn" type="button" class="btn btn-default">开始上传</button> --></div></div></div></div><div class="control-group"><label class="control-label">视频上传:</label><div class="controls"><div id="uploader" class="wu-example"><!--用来存放文件信息--><div id="thelist" class="uploader-list o-h"></div><div class="btns"><div id="picker" class="pull-left">选择视频<!-- <input type="file" class="webuploader-container"> --></div><!-- <button id="ctlBtn" type="button" class="btn btn-default">开始上传</button> --></div></div></div></div><div class="btnBox"><button class="btn btn-primary" id="ctlBtn">上传并提交</button><button type="button" class="btn btn-default" id="close">关闭</button></div></form:form><script type="text/javascript">$(function () {var iframeIndex = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引zcityrun('.zcityGroup');//省市联动$("#close").click(function(){parent.layer.close(iframeIndex);});$list = $('#thelist');var code = "";//封面图片地址var examineP;//审核人员var count = 0;var flie_count = 0;var classesId = "${tcId}";var uploadType = 'xlsx';var isupload = false;//上传封面图片var uploaderimg = WebUploader.create({//设置选完文件后是否自动上传auto: false,//swf文件路径swf:  '${ctxStatic}/webuploader/Uploader.swf',// 文件接收服务端。server: "${ctx}/uploadfiles/mediaUploadInfo/sliceUploadFiles",// 选择文件的按钮。可选。// 内部根据当前运行是创建,可能是input元素,也可能是flash.pick: '#pickerimg',chunked: true, //开启分块上传chunkSize: 10 * 1024 * 1024,chunkRetry: 3,//网络问题上传失败后重试次数threads: 3, //上传并发数//fileNumLimit :1,fileSizeLimit: 1024*1024*1024*5,//最大2GB//fileSingleSizeLimit: ltsot*1024*1024*1024*1024,resize: false,//不压缩compress:false, //不压缩源文件//选择文件类型accept : {title: 'Images', extensions : 'bmp,jpg,png,gif,',         mimeTypes: '/*'  },});uploaderimg.on("error", function (type) {if (type == "Q_TYPE_DENIED") {layer.alert('请上传正确文件格式!', {icon: 2,closeBtn: false});}else if (type == "Q_EXCEED_SIZE_LIMIT") {layer.alert('上传文件不能超过2G!', {icon: 2,closeBtn: false});}else {layer.alert('上传出错!请检查后重新上传!错误代码'+type, {icon: 2,closeBtn: false});}});var index = 0;uploaderimg.onFileQueued = function( file ) { //文件添加到对列的监听console.log(file);var fileContent = uploaderimg.getFiles();index++;if(index > 1){uploaderimg.removeFile(fileContent[fileContent.length-2]);}var $li = $('<div id="' + file.id + '" class="item m-b-10" style="border-bottom: 1px solid #eee;">' + '<h4 class="info"><img></h4>' + '<p class="state m-t-5">文件自动上传准备中...</p><input type="hidden" id="s_WU_FILE_'+flie_count+'" />' + '</div>');flie_count++;var $img = $li.find('img');$("#imglist").html($li);uploaderimg.makeThumb( file, function( error, src ) {if ( error ) {$img.replaceWith('<span>不能预览</span>');return;}$img.attr( 'src', src );}, 100, 100 );//md5计算uploaderimg.md5File(file).progress(function(percentage) {// console.log('Percentage:', percentage);})// 完成.then(function (fileMd5) { // 完成var end = +new Date();//console.log("before-send-file  preupload: file.size="+file.size+" file.md5="+fileMd5);file.wholeMd5 = fileMd5;//获取到了md5//uploader.options.formData.md5value = file.wholeMd5;//每个文件都附带一个md5,便于实现秒传console.log(file);$('#' + file.id).find('p.state').text('MD5计算完毕,将自动上传请耐心等待');uploaderimg.upload(file);//上传//console.info("MD5="+fileMd5);});};// 文件上传过程中创建进度条实时显示。// uploaderimg.on('uploadProgress', function (file, percentage) {//     var $li = $('#' + file.id),//             $percent = $li.find('.progress .progress-bar');//     // 避免重复创建//     if (!$percent.length) {//         $percent = $('<div class="progress progress-striped active">' +//                 '<div class="progress-bar" role="progressbar" style="width: 0%">' +//                 '</div>' +//                 '</div>').appendTo($li).find('.progress-bar');//     }//     $li.find('p.state').text('已上传'+Math.floor(percentage*100)+"%").css("color","#555");//     $percent.css('width', percentage * 100 + '%');// });uploaderimg.on( 'uploadBeforeSend', function( block, data ) {var file = block.file;var fileMd5 = file.wholeMd5;console.log(data);data.uploadType = $("#uploadType").val();;data.UUID = "${UUID}";data.md5Value = fileMd5;data.uploadCounts = flie_count;data.chunks = block.chunks; //总分片数data.chunk = block.chunk; //当前第几片data.isCover = true;data.isVideo = true;});uploaderimg.on( 'uploadSuccess', function( file,res ) {console.log(res);code = res.code;$('#' + file.id).find('p.state').text('已上传').css("color","green");$('#' + file.id).find(".progress").find(".progress-bar").attr("class", "progress-bar progress-bar-success");});uploaderimg.on( 'uploadError', function( file ) {$('#' + file.id).find('p.state').text('上传出错').css("color","red");//上传出错后进度条变红$('#' + file.id).find(".progress").find(".progress-bar").attr("class", "progress-bar progress-bar-danger");//添加重试按钮//为了防止重复添加重试按钮,做一个判断//var retrybutton = $('#' + file.id).find(".btn-retry");//$('#' + file.id)if ($('#' + file.id).find(".btn-retry").length < 1) {var btn = $('<button type="button" fileid="' + file.id + '" class="btn btn-success btn-retry m-l-5">重试</button>');$('#' + file.id).find(".info").append(btn);//.find(".btn-danger")}$(".btn-retry").click(function () {//console.log($(this).attr("fileId"));//拿到文件iduploaderimg.retry(uploaderimg.getFile($(this).attr("fileId")));});});uploaderimg.on( 'uploadComplete', function( file ) {$('#' + file.id).find('.progress').fadeOut();//上传完删除进度条});// 上传视频var uploader = WebUploader.create({//设置选完文件后是否自动上传auto: false,//swf文件路径swf:  '${ctxStatic}/webuploader/Uploader.swf',// 文件接收服务端。server: "${ctx}/uploadfiles/mediaUploadInfo/sliceUploadFiles",// 选择文件的按钮。可选。// 内部根据当前运行是创建,可能是input元素,也可能是flash.pick: '#picker',chunked: true, //开启分块上传chunkSize: 10 * 1024 * 1024,chunkRetry: 3,//网络问题上传失败后重试次数threads: 3, //上传并发数//fileNumLimit :1,fileSizeLimit: 1024*1024*1024*10,//最大10GB//fileSingleSizeLimit: ltsot*1024*1024*1024*1024,resize: false,//不压缩//选择文件类型accept : {title: 'vedio', extensions : 'mp4,rm,rmvb,mpeg1-4,mov,mtv,wmv,avi,3gp,amv,dmv,flv,ogg',          mimeTypes: 'vedio/*'  },//accept: {//    title: 'Video',//    extensions: 'mp4,avi',//    mimeTypes: 'video/*'//}});// $("#ctlBtn").click(function(){//     layer.msg('捕获就是从页面已经存在的元素上,包裹layer的结构', {icon:2,time:0,btn:'确定'});// });/*** 验证文件格式以及文件大小*/uploader.on("error", function (type) {if (type == "Q_TYPE_DENIED") {layer.alert('请上传正确文件格式!', {icon: 2,closeBtn: false});}else if (type == "Q_EXCEED_SIZE_LIMIT") {layer.alert('上传文件总大小不能超过10G!', {icon: 2,closeBtn: false});}else {layer.alert('上传出错!请检查后重新上传!错误代码'+type, {icon: 2,closeBtn: false});}});// 当有文件被添加进队列的时候uploader.on('fileQueued', function (file) {console.log(file);var $li = $('<div id="' + file.id + '" class="item m-b-10" style="border-bottom: 1px solid #eee;">' +'<h4 class="info">' + file.name + '<button type="button" fileId="' + file.id + '" class="btn btn-danger btn-delete m-l-5">删除</button></h4>' + '<p class="state ready">文件准备中...</p><input type="hidden" id="s_WU_FILE_'+flie_count+'" />' +'</div>');//console.info("id=file_"+flie_count);flie_count++;$list.append($li);//删除要上传的文件//每次添加文件都给btn-delete绑定删除方法$(".btn-delete").click(function () {//console.log($(this).attr("fileId"));//拿到文件iduploader.removeFile(uploader.getFile($(this).attr("fileId"), true));$(this).parent().parent().fadeOut();//视觉上消失了$(this).parent().parent().remove();//DOM上删除了});//uploader.options.formData.guid = WebUploader.guid();//每个文件都附带一个guid,以在服务端确定哪些文件块本来是一个//console.info("guid= "+WebUploader.guid());//md5计算uploader.md5File(file).progress(function(percentage) {$li.find('p.state').text('文件准备中...'+Math.floor(percentage*100)+"%").css("color","#555");// console.log('Percentage:', percentage);})// 完成.then(function (fileMd5) { // 完成var end = +new Date();//console.log("before-send-file  preupload: file.size="+file.size+" file.md5="+fileMd5);file.wholeMd5 = fileMd5;//获取到了md5//uploader.options.formData.md5value = file.wholeMd5;//每个文件都附带一个md5,便于实现秒传$('#' + file.id).find('p.state').text('等待上传').removeClass("ready").css("color","#555");// uploader.upload(file);//上传isupload = true;//console.info("MD5="+fileMd5);});});//文件上传过程中创建进度条实时显示。uploader.on('uploadProgress', function (file, percentage) {var $li = $('#' + file.id),$percent = $li.find('.progress .progress-bar');// 避免重复创建if (!$percent.length) {$percent = $('<div class="progress progress-striped active">' +'<div class="progress-bar" role="progressbar" style="width: 0%;">' +'</div>' +'</div>').appendTo($li).find('.progress-bar');}$li.find('p.state').removeClass("ready").text('已上传'+Math.floor(percentage*100)+"%").css("color","#555");$percent.css('width', percentage * 100 + '%');});//发送前填充数据uploader.on( 'uploadBeforeSend', function( block, data ) {console.log(block,data);// block为分块数据。// file为分块对应的file对象。var file = block.file;var fileMd5 = file.wholeMd5;// 修改data可以控制发送哪些携带数据。data.uploadCounts = count;//文件个数//console.info("fileName= "+file.name+" fileMd5= "+fileMd5+" fileId= "+file.id);//console.info("input file= "+ flie_count);// 将存在file对象中的md5数据携带发送过去。data.md5Value = fileMd5;//md5// data.fileName_ = $("#s_"+file.id).val();//console.log("fileName_: "+data.fileName_);// 删除其他数据// delete data.key;// if(block.chunks>1){ //文件大于chunksize 分片上传//     data.isChunked = true;//    // console.info("data.isChunked= "+data.isChunked);// }else{//     data.isChunked = false;//     //console.info("data.isChunked="+data.isChunked);// }// data.uploadAuditor = examineP[0].userid; //审核人员data.uploadType = $("#uploadType").val(); //文件类型data.uploadTitle = $("#uploadTitle").val(); //上传标题data.shootAuthor = $("#shootAuthor").val(); //摄影师data.shootTime = $("#shootTime").val(); //拍摄时间data.shootAddress = $(".zcityItem[item-level='1']").find(".currentValue").val() + $(".zcityItem[item-level='2']").find(".currentValue").val() + $("#shootplace").val(); //拍摄地址data.remarks = $("#remarks").val(); //备注data.shareStatus = $('input:radio[name="shareStatus"]:checked').val(); //共享状态data.filePixel = ""; //像素data.fileRp = ""; //分辨率// data.fileSize = file.size; //分片文件大小data.fileNum = ""; //文件编号data.UUID = "${UUID}"; //随机生成的UUIDdata.isCover = false; //是否为封面data.chunks = block.chunks; //总分片数data.chunk = block.chunk; //当前第几片// data.currentFileId= file.id; //当前文件IDdata.name = file.name; //文件名data.isVideo = true;// data.file = ""; //文件});uploader.on('uploadSuccess', function (file,data) {console.log(data);$('#' + file.id).find('p.state').removeClass("ready").text('已上传').css("color","green");$('#' + file.id).find(".progress").find(".progress-bar").attr("class", "progress-bar progress-bar-success");$('#' + file.id).find(".info").find('.btn').fadeOut('slow');//上传完后删除"删除"按钮$('#StopBtn').fadeOut('slow');});uploader.on('uploadError', function (file) {$('#' + file.id).find('p.state').removeClass("ready").text('上传出错').css("color","red");//上传出错后进度条变红$('#' + file.id).find(".progress").find(".progress-bar").attr("class", "progress-bar progress-bar-danger");//添加重试按钮//为了防止重复添加重试按钮,做一个判断//var retrybutton = $('#' + file.id).find(".btn-retry");//$('#' + file.id)if ($('#' + file.id).find(".btn-retry").length < 1) {var btn = $('<button type="button" fileid="' + file.id + '" class="btn btn-success btn-retry m-l-5">重试</button>');$('#' + file.id).find(".info").append(btn);//.find(".btn-danger")}$(".btn-retry").click(function () {//console.log($(this).attr("fileId"));//拿到文件iduploader.retry(uploader.getFile($(this).attr("fileId")));});});uploader.on('uploadComplete', function (file) {//上传完成后回调$('#' + file.id).find('.progress').fadeOut();//上传完删除进度条$('#' + file.id + 'btn').fadeOut('slow')//上传完后删除"删除"按钮});uploader.on('uploadFinished', function () {var list = $("#thelist").find(".state");var flag = true;for(var i=0;i<list.length;i++){if($(list[i]).text() == "等待上传" || $(list[i]).text() == "上传出错"){flag = false;}}if(flag){layer.alert('上传成功,请等待审核!', {icon: 1,closeBtn: false}, function () {parent.layer.close(iframeIndex);});}else{layer.alert('有视频上传出错', {icon: 2,closeBtn: false});}//上传完后的回调方法// layer.msg('所有文件上传完毕', {icon:1,time:0,btn:'确定'});//提交表单});// $("#ctlBtn").click(function () {//     if(!isupload){//        layer.msg('请耐心等待', {icon:2,time:0,btn:'确定'});//     }else{//        uploader.upload();//     }// });$("#StopBtn").click(function () {//console.log($('#StopBtn').attr("status"));var status = $('#StopBtn').attr("status");if (status == "suspend") {//console.log("当前按钮是暂停,即将变为继续");$("#StopBtn").html("继续上传");$("#StopBtn").attr("status", "continuous");// console.log("当前所有文件==="+uploader.getFiles());//console.log("=============暂停上传==============");uploader.stop(true);// console.log("=============所有当前暂停的文件=============");// console.log(uploader.getFiles("interrupt"));} else {//console.log("当前按钮是继续,即将变为暂停");$("#StopBtn").html("暂停上传");$("#StopBtn").attr("status", "suspend");//console.log("===============所有当前暂停的文件==============");// console.log(uploader.getFiles("interrupt"));uploader.upload(uploader.getFiles("interrupt"));}});uploader.on('uploadAccept', function (file, response) {if (response._raw === '{"error":true}') {return false;}});$("#inputForm").validate({submitHandler: function(form){// loading('正在提交,请稍等...');if(code == "100"){if(uploader.getFiles('inited').length == 0){layer.alert('请选择视频', {icon: 2,closeBtn: false});}else{if($("#thelist").find("p.ready").length == 0){ //文件准备完毕才能上传count = uploader.getFiles('inited').length;uploader.upload();}else{layer.alert('文件正在准备中,待文件准备完毕,再点击上传', {icon: 2,closeBtn: false});}}}else{layer.alert('请上传封面图片', {icon: 2,closeBtn: false});}// form.submit();},errorContainer: "#messageBox",errorPlacement: function(error, element) {$("#messageBox").text("输入有误,请先更正。");if (element.is(":checkbox")||element.is(":radio")||element.parent().is(".input-append")){error.appendTo(element.parent().parent());} else {error.insertAfter(element);}}});// 选人按钮// $("#btn-select").click(function(){//        layer.open({//        type: 2//        ,area: ["600px", '100%']//        ,title: '选人'//        ,content: '${ctx}/sys/user/select'//        ,closeBtn: false//     });// });// window.selecFuc = function(data){//     examineP = data;//     $("#examineP").val(examineP[0].userName);// }});</script>
</body>
</html>

java实现大文件分片上传相关推荐

  1. minio实现大文件分片上传+断点续传+预览

    minio实现大文件分片上传+断点续传+预览 只提供后端java代码 思路: 前端分片 校验文件md5是否已经存在 --不存在创建临时桶存分片 校验分块是否已经上传 分块上传 合并分块 校验合成后md ...

  2. 大文件分片上传前后端实现

    最近在做公司的视频业务,涉及到大视频的上传. 之前的图片.Excel等上传做的很简单,直接表单提交后端用MultipartFile接收保存到磁盘就行了. 但是针对大文件的上传,需要做额外的处理,否则可 ...

  3. AWS-S3通用存储操作,操作minio、oss、cos等所有兼容s3协议的云存储(含有大文件分片上传实现)

    一.介绍 通用存储操作common包,支持所有兼容amazon-s3协议的云存储,如minio.oss.cos等,以后客户用啥云储存一套代码都能搞定了,真棒~ 二.代码结构 三.代码实现 3.1 po ...

  4. 大文件分片上传,断点续传,秒传 实现

    前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...

  5. jquery 分片上传php,php 大文件分片上传

    前端部分 上传 //上传控件 uploadBig('upload','zip,rar,7z,tar',{ id: '', type: 'upload_file', } ,(res)=>{ //t ...

  6. 大文件分片上传前端框架_基于Node.js的大文件分片上传

    基于Node.js的大文件分片上传 我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况.所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作.同时如果文件过大,在网络不佳 ...

  7. Vue项目中遇到了大文件分片上传的问题

    Vue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽. 上传就上传吧,为什么 ...

  8. 大文件分片上传前端框架_无插件实现大文件分片上传,断点续传

    文件上传.gif 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试上传等需求的使用场景及实现: 2. 项目需求 在一个音视频的添加中,既要有音视频 ...

  9. 无插件实现大文件分片上传,断点续传

    代码地址如下: http://www.demodashi.com/demo/11888.html 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试 ...

最新文章

  1. BZOJ 4086: [Sdoi2015]travel(SDOI2015 round2 day1)(分类讨论+容斥原理)
  2. webpack笔记(2)打包src下的html文件
  3. cobaltstrike安装_CobaltStrike + Metasploit 组合安装
  4. Qt for ios 打开相机(添加权限)
  5. 关于时钟、中断的理解
  6. 写出float x 与“零值”比较的if语句——一道面试题分析
  7. python计算机代码_python告白代码,只属于程序员的浪漫
  8. 呐,一个苹果洞赚10万美元的详细经验都在这里了~
  9. RRT算法原理和代码详解(快速扩展随机树)
  10. android动态表格数据类型,华为运动表GT表盘主题制作教程
  11. 凤凰系统基于android x x86,凤凰系统X86|Phoenix OS X86 V3.0.8.529官方版
  12. 用vuejs如何实现ajax,vuejs使用FormData实现ajax上传图片文件
  13. java调用考勤机_zkteco iface702 中控考勤机java开发步骤一---连接考勤机
  14. 图片上传几种方式总结
  15. 安卓实时打印kernel日志
  16. 固态硬盘启动蓝屏解决方法
  17. catia导出运动html,CATIA使用DMU生成运动轨迹包络面 | 坐倚北风
  18. 编程中的心流模式flow
  19. 2023版golang面试题100道(map)
  20. JS原生轮播(JS篇)

热门文章

  1. Unity打包安卓如何存储本地游戏数据?
  2. Java-获取当天凌晨的时间戳
  3. php源码 收据管理,黄金票据 白银票据 ms14068(示例代码)
  4. 获取网易云音乐接口以及使用
  5. 编程语言1月排行榜:C是年度语言,Python增长量第二
  6. 线切割原理及应用(钣金)
  7. 安卓手机小说阅读器_【趣读小说大全APP安卓】趣读小说大全手机app下载 v1.0.0 免费版...
  8. win7字体_免费字体?别再乱找了,这里应有尽有
  9. SHOUTcast使用指南(1)
  10. sed: -e expression #1, char 8: unterminated `s‘ command