java實現大文件分片上傳
在項目中用到了大文件上傳功能,最初從網上參考了一些代碼來實現,但是最終的上傳效果不是很好,速度比較慢。
之前的上傳思路是:
- 前端利用webUploader分片大文件
- 後端接收各個分片後的小文件
- 接收完一個大文件的所有分片文件後,合併這些文件爲大文件
現在的思路是:
-
前端利用webUploader分片大文件
-
後端接收各個分片後的小文件
-
將接收到的小文件直接存到目標文件夾
-
需要注意的是,一定要按分片順序來存儲小文件,不然最後生成的
文件和原文件不一樣,會被損壞
代碼實現:
後端:
MediaUploadInfoController.java
/**
* Copyright © 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 {
@Autowired
private MediaUploadInfoService mediaUploadInfoService;
@Autowired
private MediaSysCompanyService mediaSysCompanyService;
@Autowired
private TypeService typeService;
@Autowired
private MediaFileInfoService mediaFileInfoService;
@ModelAttribute
public 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";
else
return "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")
@ResponseBody
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) {
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")
@ResponseBody
public 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
@Transactional
public 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 © 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;
@Autowired
private 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);
}
@Autowired
private 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("分片文件保存失敗!");
}
// 分片上傳成功總數增加1
chunkUploadCount = 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);
}
//用來更新info
mediaUploadInfo.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)); //單位KB
mediaFileInfo.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的list
List<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("/");
}
}else
throw 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);
}else
this.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);
//用來更新info
mediaUploadInfo.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);
// 構造一個類型爲預定義圖像類型之一的 BufferedImage
BufferedImage 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;
}else
return 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 array
while ((len = inputStream.read(buffer)) != -1) {
//Writes <code>len</code> bytes from the specified byte array starting at offset <code>off</code> to this output stream
outputStream.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 destination
outputStream.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">文件屬於 ${company.name}:</label>
<div class="controls" style="padding-top: 3px;">
<input type="radio" id="share" name="shareStatus" value="0" checked/><label for="share" >共享</label>
<input type="radio" id="nonShare" name="shareStatus" value="1" /><label for="nonShare">不共享</label>
</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"));//拿到文件id
uploaderimg.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"));//拿到文件id
uploader.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}"; //隨機生成的UUID
data.isCover = false; //是否爲封面
data.chunks = block.chunks; //總分片數
data.chunk = block.chunk; //當前第幾片
// data.currentFileId= file.id; //當前文件ID
data.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"));//拿到文件id
uploader.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>