基于WebUploader、SpringMVC的断点续传
程序员文章站
2024-02-19 08:31:40
...
页面引用:
<!--引用tag标签路径,并定义为sys-->
<%@ taglib prefix="sys" tagdir="/WEB-INF/tags/sys" %>
<!--以下两个隐藏域为spring标签,可以用常规html代替,用以存放上传文件返回的文件真实地址和文件访问地址-->
<form:hidden id="hzzqtysUrl" path="hzzqtysUrl" htmlEscape="false" maxlength="255" class="input-xlarge required"/>
<form:hidden id="hzzqtysTitle" path="hzzqtysTitle" htmlEscape="false" maxlength="255" class="input-xlarge"/>
<!--前端引用tag组件方法-->
<sys:webuploader input="hzzqtysUrl" label="hzzqtysTitle" type="all"/>
tag组件:webuploader.tag
<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<%@ attribute name="input" type="java.lang.String" required="true" description="文件路径用'|'分割组成"%>
<%@ attribute name="label" type="java.lang.String" required="true" description="文件名称用'|'分割组成"%>
<%@ attribute name="type" type="java.lang.String" required="true" description="images、docs、all"%>
<%@ attribute name="fileSingleSizeLimit" type="java.lang.String" required="false" description="单文件最大尺寸(kb)"%>
<%@ attribute name="fileSizeLimit" type="java.lang.String" required="false" description="文件总大小最大尺寸(kb)"%>
<%@ attribute name="chunkSize" type="java.lang.String" required="false" description="分片大小(kb),默认大小为5M"%>
<%@ attribute name="fileNumLimit" type="java.lang.Integer" required="false" description="文件总数量"%>
<%@ attribute name="readonly" type="java.lang.Boolean" required="false" description="是否查看模式"%>
<%@ attribute name="maxWidth" type="java.lang.String" required="false" description="最大宽度"%>
<%@ attribute name="maxHeight" type="java.lang.String" required="false" description="最大高度"%>
<link href="${ctxStatic }/webuploader-0.1.5/webuploader.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="${ctxStatic }/webuploader-0.1.5/webuploader.js"></script>
<input id="${input}jindutiao" type="hidden" />
<input id="${input}fileIds" type="hidden" />
<div id="${input}uploader" class="wu-example">
<div class="btns">
<div id="${input}filePicker" class="webuploader-container">
<div class="webuploader-pick"> 上传 </div>
<input id="${input}file_bp" name="file"
class="webuploader-element-invisible" type="file" />
</div>
<!-- 文件列表:选择文件后在该div显示 -->
<ol id="${input}fileList"></ol>
<label class="text-right"
style="font-weight: 100; float: left; margin-left: 15px; width: 144px; margin-right: 15px;"></label>
</div>
</div>
<script type="text/javascript">
var preShowPath = $("#preShowPath").val();//访问文件前缀
var ${input}_$md5Array = new Array(), // 文件的MD5
${input}_$nameArray = new Array(), // 文件名称
${input}_count = 0, // 正在上传的文件在上传列表中的位置
${input}_success=0,//上传成功的文件数
${input}_map={},//key存储文件id,value存储该文件上传过的进度
${input}_uploader; // Web Uploader实例
var ${input}_webUploader = WebUploader;
$(function() {
// 监听分块上传的三个事件
${input}_webUploader.Uploader.register({
"before-send-file" : "${input}_beforeSendFile", // 所有分块上传之前
"before-send" : "${input}_beforeSend", // 每个分块上传之前
"after-send-file" : "${input}_afterSendFile" // 所有分块上传完成后
},{
${input}_beforeSendFile : function(file){
var deferred = ${input}_webUploader.Deferred();
// 计算文件的MD5
${input}_uploader.md5File(file,0,10*1024*1024)
// 及时显示进度
.progress(function(percentage) {
// $('#'+file.id ).find('p.state').text('正在读取文件信息...');
})
// 计算完成,继续下一步
.then(function(val) {
// $('#'+file.id ).find("p.state").text("成功获取文件信息...");
${input}_$md5Array.push(val);
${input}_$nameArray.push(file.name);
deferred.resolve();
});
return deferred.promise();
},
${input}_beforeSend : function(block){
var deferred = ${input}_webUploader.Deferred();
// 每个分块上传之前校验是否已上传
var url = "${ctx}/uploadBig/check",
param = {
fileName : ${input}_$nameArray[${input}_count],
fileMd5 : ${input}_$md5Array[${input}_count],
jindutiao:$("#${input}jindutiao").val(),
chunk : block.chunk,
chunkSize : block.end - block.start
};
// 同步校验,防止没校验完就上传了
$.ajaxSetup({async : false});
$.post(url,param,function(data){
var res = eval('('+data+')');
// 已上传则跳过,否则继续上传
if(res.ifExist){
deferred.reject();
}else{
deferred.resolve();
}
},"json");
$.ajaxSetup({async : true});
this.owner.options.formData.fileMd5 = ${input}_$md5Array[${input}_count];
deferred.resolve();
return deferred.promise();
},
${input}_afterSendFile : function(file){
// 所有分块上传完毕,通知后台合并分块
var url = "${ctx}/uploadBig/merge",
// 上传前设置其它参数
param = {
fileMd5 : ${input}_$md5Array[${input}_count],
fileName : ${input}_$nameArray[${input}_count]
};
$.ajaxSetup({async : false});
$.post(url,param,function(data){
var urlFilePaths =data.fileUrl;
var urlFileNames = file.name;
var fileIds = file.id;
preShowPath = data.preShowPath;
//单选或多选模式
<c:if test="${fileNumLimit eq 1}">
$("#${input}").val(urlFilePaths);
$("#${label}").val(urlFileNames);
$("#${input}fileIds").val(fileIds);
</c:if>
<c:if test="${fileNumLimit ne 1}">
var origFilePaths = $("#${input}").val();
var origFileNames = $("#${label}").val();
var origFileIds = $("#${input}fileIds").val();
if(""==origFilePaths){
$("#${input}").val(urlFilePaths);
}else{
$("#${input}").val(origFilePaths + "|"+ urlFilePaths);
}
if(""==origFileNames){
$("#${label}").val(urlFileNames);
}else{
$("#${label}").val(origFileNames + "|"+ urlFileNames);
}
if(""==origFileIds){
$("#${input}fileIds").val(fileIds);
}else{
$("#${input}fileIds").val(origFileIds + "|"+ fileIds);
}
</c:if>
${input}Preview(${input}_map[file.id]);
${input}_count++;
${input}_uploader.upload(file);
},"json");
$.ajaxSetup({async : true});
}
});
// 初始化Web Uploader PS:IE使用的flash上传,真心慢,大文件还是用Chrome上传比较靠谱
${input}_uploader = ${input}_webUploader.create({
auto : true, // 自动上传
swf : '${ctxStatic}/webuploader-0.1.5/Uploader.swf',
server : '${ctx}/uploadBig/upload', // 文件接收服务端
threads : 1, // 只运行1个线程传输
duplicate : false, // 是否重复上传(单次选择同样的文件)
prepareNextFile : true, // 允许在文件传输时提前把下一个文件准备好
chunked : true, // 是否要分片处理大文件上传
<c:if test="${not empty chunkSize}">
chunkSize : ${chunkSize}, // 如果要分片,分多大一片? 10M默认大小为5M
</c:if>
fileNumLimit : ${empty fileNumLimit? 10:fileNumLimit}, // 文件总数量
<c:if test="${not empty fileSingleSizeLimit}">
fileSingleSizeLimit : ${fileSingleSizeLimit}, // 单个文件大小限制
</c:if>
<c:if test="${not empty fileSizeLimit}">
fileSizeLimit : ${fileSizeLimit}, // 文件总大小限制
</c:if>
pick : {
id : '#${input}filePicker', // 选择文件的按钮
multiple : false // 是否允许同时选择多个文件
},
compress: false, // 不压缩文件
<c:if test="${type eq 'images'}">
accept : {
extensions: "gif,jpg,jpeg,bmp,png",
mimeTypes: '.gif,.jpg,.jpeg,.bmp,.png',
}
</c:if>
<c:if test="${type eq 'docs'}">
accept : {
extensions: "txt,pdf,cebx,doc,docx,ppt,pptx,xls,xlsx",
mimeTypes: '.txt,.pdf,.cebx,.doc,.docx,.ppt,.pptx,.xls,.xlsx',
}
</c:if>
<c:if test="${type eq 'all'}">
accept : {
// 常见视频文件格式:avi,wmv,mpeg,mp4,mov,mkv,flv,f4v,m4v,rmvb,rm,3gp,dat,ts,mts,vob
extensions: "txt,gif,jpg,jpeg,bmp,png,zip,rar,war,pdf,cebx,doc,docx,ppt,pptx,xls,xlsx",
mimeTypes: '.txt,.gif,.jpg,.jpeg,.bmp,.png,.zip,.rar,.war,.pdf,.cebx,.doc,.docx,.ppt,.pptx,.xls,.xlsx',
}
</c:if>
});
// 当有文件添加进来的时候
${input}_uploader.on('fileQueued', function(file) {
//进度条显示
$("#mask").css("height",$(document).height());
$("#mask").css("width",$(document).width());
$("#mask").show();
${input}_success++;
$.ajax({
type:"POST",
url:"${ctx}/uploadBig/selectProgressByFileName", //先检查该文件是否上传过,如果上传过,上传进度是多少
data:{
fileMD5 : ${input}_$md5Array[${input}_count] //文件MD5
},
cache: false,
async: false, // 同步
//dataType:"json",
success:function(result){
var res = eval('('+result+')');
//上传过
if(res.jindutiao>0){
//将上传过的进度存入map集合
${input}_map[file.id]=res.jindutiao/100;
}
$("#${input}filePicker").append('<div id="${input}'+file.id+'1"></div>');
}
});
${input}_uploader.stop(true);
});
// 上传中
${input}_uploader.on('uploadProgress', function (file, percentage) {
var $li = $( '#${input}'+file.id+'1' ),
$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%;height:20px; background-color:#2fa4e7;">' +
'</div>' +
'</div>').appendTo( $li ).find('.progress-bar');
}
//将实时进度存入隐藏域
$("#${input}jindutiao").val(Math.round(percentage * 100));
//根据fielId获得当前要上传的文件的进度
var oldJinduValue = ${input}_map[file.id];
if(percentage<oldJinduValue && oldJinduValue!=1){
// $li.find('p.state').text('上传中'+Math.round(oldJinduValue * 100) + '%');
$percent.text(Math.round(oldJinduValue * 100) + '%');
$percent.css('width', oldJinduValue * 100 + '%');
}else{
// $li.find('p.state').text('上传中'+Math.round(percentage * 100) + '%');
$percent.text(Math.round(percentage * 100) + '%');
$percent.css('width', percentage * 100 + '%');
}
});
// 文件上传成功
${input}_uploader.on('uploadSuccess', function(file) {
//上传成功去掉进度条
$('#${input}'+file.id+'1').find('.progress').fadeOut();
// $('#'+file.id).find('p.state').text('');
//隐藏进度条
$("#mask").hide();
});
// 文件上传失败
${input}_uploader.on('uploadError', function(file) {
${input}_uploader.stop(true);
// $('#'+file.id).find('p.state').text('上传出错,请检查网络连接');
$.jBox.tip('上传出错,请检查网络连接');
//隐藏进度条
$("#mask").hide();
});
/**
* 验证文件格式以及文件大小
*/
${input}_uploader.on("error",function (type){
if (type=="Q_TYPE_DENIED"){
$.jBox.tip('请上传正确格式的文件');
}else if(type=="F_EXCEED_SIZE"){
$.jBox.tip('请上传正确大小的文件');
}
});
});
function ${input}Del(obj){
var newFilePaths = "";
var newFileNames = "";
var newFileIds = "";
var url = $(obj).prev().attr("url");
var origFilePaths = $("#${input}").val().split("|");
var origFileNames = $("#${label}").val().split("|");
var origFileIds = $("#${input}fileIds").val().split("|");
if(origFilePaths.length == origFileNames.length){
for (var i=0; i<origFilePaths.length; i++){
if (preShowPath+origFilePaths[i]!=url){
if(""==newFilePaths){
newFilePaths = origFilePaths[i];
}else{
newFilePaths +="|"+origFilePaths[i];
}
if(""==newFileNames){
newFileNames = origFileNames[i];
}else{
newFileNames +="|"+origFileNames[i];
}
if(""==newFileIds){
newFileIds = origFileIds[i];
}else{
newFileIds +="|"+origFileIds[i];
}
}
}
}else{
$.jBox.alert('数据初始化出现异常: 数组长度不一致', '失败');
}
$("#${input}").val(newFilePaths);
$("#${label}").val(newFileNames);
$("#${input}fileIds").val(newFileIds);
try{
var fileItem = $(obj).parent();
${input}_uploader.removeFile($(fileItem).attr("id"), true);
}catch(err){
console.log('修改页面默认因为只是显示图片,删除操作可能报错,不影响整体功能');
}
${input}Preview(0);
}
function ${input}Preview(bar){
var tmp, urlFileNames = $("#${label}").val().split("|");
var li, fileId, urls = $("#${input}").val().split("|")
,fileIds = $("#${input}fileIds").val().split("|");
$("#${input}fileList").children().remove();
//上传过
if(bar>0){
//如果上传过 上传了多少
var jindutiaoStyle="width:"+bar+"%";
for (var i=0; i<urls.length; i++){
if (urls[i]!=""){
tmp = urls[i].substring(urls[i].lastIndexOf("/")+1);
if(typeof(fileIds[i])=='undefined'){
fileId = "WU_FILE_"+i;
}else{
fileId = fileIds[i];
}
if(urlFileNames.length==urls.length){
tmp = urlFileNames[i];
}
li = '<li id="'+fileId+'"><a href="'+preShowPath+urls[i]+'?filename='+decodeURIComponent(tmp)+'" url="'+preShowPath+urls[i]+'" target="_blank">';
<c:choose>
<c:when test="${type eq 'images'}">
li += '<img src="'+preShowPath+urls[i]+'" url="'+preShowPath+urls[i]+'" style="max-width:${empty maxWidth ? 200 : maxWidth}px;max-height:${empty maxHeight ? 200 : maxHeight}px;_height:${empty maxHeight ? 200 : maxHeight}px;border:0;padding:3px;">';
</c:when>
<c:otherwise>
li += decodeURIComponent(tmp);
</c:otherwise>
</c:choose>
li += '</a> <c:if test="${!readonly}"><a href="javascript:" onclick="${input}Del(this);">×</a></c:if></li>'+
'<div class="progress progress-striped active">' +
'<div class="progress-bar" role="progressbar" style="'+jindutiaoStyle+'">';
$("#${input}fileList").append(li);
}
}
}else{
for (var i=0; i<urls.length; i++){
if (urls[i]!=""){
tmp = urls[i].substring(urls[i].lastIndexOf("/")+1);
if(typeof(fileIds[i])=='undefined'){
fileId = "WU_FILE_"+i;
}else{
fileId = fileIds[i];
}
if(urlFileNames.length==urls.length){
tmp = urlFileNames[i];
}
li = '<li id="'+fileId+'"><a href="'+preShowPath+urls[i]+'?filename='+decodeURIComponent(tmp)+'" url="'+preShowPath+urls[i]+'" target="_blank">';
<c:choose>
<c:when test="${type eq 'images'}">
li += '<img src="'+preShowPath+urls[i]+'" url="'+preShowPath+urls[i]+'" style="max-width:${empty maxWidth ? 200 : maxWidth}px;max-height:${empty maxHeight ? 200 : maxHeight}px;_height:${empty maxHeight ? 200 : maxHeight}px;border:0;padding:3px;">';
</c:when>
<c:otherwise>
li += decodeURIComponent(tmp);
</c:otherwise>
</c:choose>
li += "</a> <c:if test="${!readonly}"><a href=\"javascript:\" onclick=\"${input}Del(this);\">×</a></c:if></li>";
$("#${input}fileList").append(li);
}
}
}
}
${input}Preview(0);
</script>
java后台代码:BigFileUploadController.java
其中包含上传到FastDFS的方法(如果不需要可以把这部分代码去掉),感觉不太好,因为是先上传到服务器中的临时路径,然后,再用FastDFS上传的,没研究出来分片之后,直接上传到FastDFS服务器中的方法,希望有大神指点。
继承的BaseController类,只是用了里面的记录日志方法和一些url前缀取值,可以忽略。
还有一些Util类,以下只提供FileUtils类,其他Util类比较普遍,项目中应该都会有。
package com.senyint.ma.common.web;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.common.servlet.entity.UploadNetStreamCallback;
import com.common.servlet.entity.UploadifyResult;
import com.common.utils.*;
import com.modules.sys.utils.UserUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.jasig.cas.client.util.CommonUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.*;
@Controller
@RequestMapping(value = "${adminPath}/uploadBig")
public class BigFileUploadController extends BaseController {
/** Fastdfs configure file **/
public static final String FASTDFS_CONIFG_FILE = "deploy.properties";
private static final String FILE_UPLOAD = "upload";
/** show files path **///e.g. http://10.66.90.176:8888
public static String SHOW_PATH = new PropertiesLoader("deploy.properties").getProperty("show_path");
/** upload files path **/e.g. windows: d:\\testUpload linux: /temp
public static String UPLOAD_PATH = new PropertiesLoader("deploy.properties").getProperty("noFdfs_upload_path");
/** 上传标识 1.fastdfs 2.ftp 3.aliyun**/
public static String UPLOAD_FLAG = new PropertiesLoader("deploy.properties").getProperty("uploadFlag");
/** Fastdfs configure file **/
protected static String SYS_CLASSES_PATH_FILE = "";
/** Temporary Path **/
protected static String SYS_TMP_PATH = "/tmp/";
protected static String SYS_FASTDFS_TRACKER_HTTP_PORT="";
/**
* 保存文件分片
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
@RequestMapping(value = "/upload")
@ResponseBody
public String upload(HttpServletRequest request, HttpServletResponse response) {
// 参数获取
String fileMd5 = request.getParameter("fileMd5");
String chunk = request.getParameter("chunk");
// 临时目录
String tempPath = request.getSession().getServletContext().getRealPath("/");//"D:\\testUpload\\upload\\";
// 转换请求对象得到文件对象
MultipartHttpServletRequest Murequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> files = Murequest.getFileMap();
if (null != files && !files.isEmpty()) {
for (MultipartFile item : files.values()) {
String tempDir = getTempDir(tempPath, fileMd5);
// 创建临时文件夹
File dir = new File(tempDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 创建分片文件并保存
File chunkFile = new File(tempDir + File.separator + chunk);
try {
chunkFile.createNewFile();
item.transferTo(chunkFile);
} catch (IllegalStateException e) {
logger.error("保存分片文件出错:" + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
logger.error("保存分片文件出错:" + e.getMessage());
e.printStackTrace();
}
}
}
return "1";
}
/**
* 校验分片是否已上传
*
* @Title: checkChunk
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
@RequestMapping(value = "/check",
method = RequestMethod.POST)
public void checkChunk(HttpServletRequest request, HttpServletResponse response) {
// 获取参数
String fileMd5 = request.getParameter("fileMd5");
String chunk = request.getParameter("chunk");
String chunkSize = request.getParameter("chunkSize");
String jindutiao = request.getParameter("jindutiao");// 文件上传的实时进度
// 临时文件
String tempPath = request.getSession().getServletContext().getRealPath("/");//"D:\\testUpload\\upload\\";
String tempDir = getTempDir(tempPath, fileMd5);
File chunkFile = new File(tempDir + File.separator + chunk);
// 将当前进度存入缓存
CacheUtils.put(FILE_UPLOAD, "jindutiao_" + fileMd5, jindutiao);
// System.out.println("缓存中的进度条="+jindutiao);
// 分片文件是否存在,尺寸是否一致
if (chunkFile.exists() && chunkFile.length() == Integer.parseInt(chunkSize)) {
writeJSON(response,"{\"ifExist\":1}");
}else{
writeJSON(response,"{\"ifExist\":0}");
}
}
/**
* 合并分片成完整文件
*
* @Title: mergeChunks
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
@SuppressWarnings("resource")
@RequestMapping(value = "/merge",
method = RequestMethod.POST)
@ResponseBody
public String mergeChunks(HttpServletRequest request, HttpServletResponse response) {
Calendar calendar=Calendar.getInstance();
String month = "";
String day = "";
SimpleDateFormat sdfM = new SimpleDateFormat("MM");
SimpleDateFormat sdfD = new SimpleDateFormat("dd");
Date ddM = Calendar.getInstance().getTime();
Date ddD = Calendar.getInstance().getTime();
month = sdfM.format(ddM);
day = sdfD.format(ddD);
String filePath = UPLOAD_PATH + File.separatorChar+calendar.get(Calendar.YEAR)+month+day;//文件存放服务器路径
String filePathUrl = "/"+calendar.get(Calendar.YEAR)+month+day;//文件访问路径
// 获取参数
String fileName = request.getParameter("fileName");
String fileMd5 = request.getParameter("fileMd5");
// 获得目标文件夹
String tempPath = request.getSession().getServletContext().getRealPath("/");//"D:\\testUpload\\upload\\";
// 创建目标文件夹
File saveDir = new File(filePath);
if (!saveDir.exists()) {
saveDir.mkdirs();
}
// 文件临时文件夹"根目录/temp/userName/文件名/MD5"
String tempDirPath = getTempDir(tempPath, fileMd5);
File tempDir = new File(tempDirPath);
// 获得分片文件列表
File[] fileArray = tempDir.listFiles(new FileFilter() {
// 只需要文件
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return false;
} else {
return true;
}
}
});
// 转成集合进行排序后合并文件
List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));
Collections.sort(fileList, new Comparator<File>() {
// 按文件名升序排列
public int compare(File o1, File o2) {
if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
return -1;
} else {
return 1;
}
}
});
String extFileName = FileUtils.getFileExtension(fileName);
String uuid = IdGen.uuid();
// 目标文件
File outfile = new File(filePath + File.separator + uuid + "." + extFileName);
try {
outfile.createNewFile();
} catch (IOException e) {
logger.error("创建目标文件出错:" + e);
}
// 执行合并操作
FileChannel outChannel = null;
FileChannel inChannel;
try {
outChannel = new FileOutputStream(outfile).getChannel();
for (File file : fileList) {
inChannel = new FileInputStream(file).getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
file.delete();
}
outChannel.close();
} catch (FileNotFoundException e) {
logger.error("合并分片文件出错:文件没找到" + e);
} catch (IOException e) {
logger.error("合并分片文件出错:读写错误" + e);
}
String fileId = filePath + File.separator + outfile.getName();
String fileUrl = filePathUrl + "/" + outfile.getName();
if(UPLOAD_FLAG.equals("fastdfs")){
fileUrl = "";
try {
//初始化FastDFS 开始
// Classes Path
File f = new File(this.getClass().getResource("/").getPath());
String classesPath = f.getAbsolutePath();
SYS_CLASSES_PATH_FILE = classesPath + File.separator + FASTDFS_CONIFG_FILE;
// Temporary Path
String tmpDir = System.getProperty("java.io.tmpdir");
if (null != tmpDir && tmpDir.trim().length() > 0) {
SYS_TMP_PATH = tmpDir.trim();
}
// Init FastDFS
ClientGlobal.init(SYS_CLASSES_PATH_FILE);
// Tracker server host name
// InetSocketAddress[] isa =ClientGlobal.g_tracker_group.tracker_servers;
// SYS_FASTDFS_HOST_NAME = isa[0].getHostName();
// Tracker server http port
SYS_FASTDFS_TRACKER_HTTP_PORT = ClientGlobal.getG_tracker_http_port() + "";
//初始化FastDFS 结束
String name = outfile.getName();
// Format
String extName = FileUtils.getFileExtension(name);
//byte[] uploadFileBuffer = StreamUtils.InputStreamTOByte(item.getInputStream());
//System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
//System.out.println("charset=" + ClientGlobal.g_charset);
// Save it into FastDFS
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient client = new StorageClient(trackerServer, storageServer);
// Set Meta data
NameValuePair[] metaList = new NameValuePair[4];
metaList[0] = new NameValuePair("fileName", name);
metaList[1] = new NameValuePair("fileExtName", extName);
metaList[2] = new NameValuePair("fileLength", String.valueOf(outfile.length()));
metaList[3] = new NameValuePair("fileUid", this.getUserId());
// Upload FastDFS
//String[] fileIds = client.upload_file(local_filename, null, metaList);
//String[] fileIds = client.upload_file(uploadFileBuffer, extName, metaList);
long fileSize = outfile.length();
UploadNetStreamCallback callback= new UploadNetStreamCallback(new FileInputStream(outfile));
String[] fileIds = client.upload_file(null, fileSize, callback, extName, metaList);
for (int i = 0; i < fileIds.length; i++) {
fileUrl = fileUrl + "/" + fileIds[i];
}
fileId = fileUrl.substring(1);
} catch (Exception ex) {
logger.error("FastDFS上传失败:",ex);
}
//已经由fastdfs存储,删除合并的文件
saveDir.delete();
}
// 删除临时文件夹
tempDir.delete();
// 合并成功后删除缓存中的进度信息
CacheUtils.remove(FILE_UPLOAD, "jindutiao_" + fileMd5);
UploadifyResult result = new UploadifyResult();
result.setFileId(fileId);//文件上传路径
result.setFileUrl(fileUrl);//文件访问路径
result.setPreShowPath(SHOW_PATH);
return JSONObject.toJSONString(result);
}
@RequestMapping(value = "selectProgressByFileName")
@ResponseBody
// 当有文件添加进队列时 通过文件名查看该文件是否上传过 上传进度是多少
public String selectProgressByFileName(String fileMD5) {
String jindutiao = "";
if (StringUtils.isNotBlank(fileMD5)) {
jindutiao = ObjectUtils.toString(CacheUtils.get(FILE_UPLOAD, "jindutiao_" + fileMD5));
}
// System.out.println("显示进度条="+jindutiao);
return "{jindutiao :'" + jindutiao + "'}";
}
/**
* 获得分片文件临时保存路径
*
* @Title: getTempDir
* @param tempPath 临时文件根目录
* @param fileMd5 文件MD5
* @return String 分片临时保存路径
*/
private String getTempDir(String tempPath, String fileMd5) {
StringBuilder dir = new StringBuilder(tempPath);
dir.append(this.getUserId());
dir.append(fileMd5);
return dir.toString();
}
/**
* 返回json格式
* @param response
* @param object
*/
public void writeJSON(HttpServletResponse response, Object object){
try {
//设定编码
response.setCharacterEncoding("UTF-8");
//表示是json类型的数据
response.setContentType("application/json");
//获取PrintWriter 往浏览器端写数据
PrintWriter writer = response.getWriter();
ObjectMapper mapper = new ObjectMapper(); //转换器
//获取到转化后的JSON 数据
String json = mapper.writeValueAsString(object);
//写数据到浏览器
writer.write(json);
//刷新,表示全部写完,把缓存数据都刷出去
writer.flush();
//关闭writer
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
logger.error("response中存json失败",e);
}
}
protected String getUserId() {
String sUid = "--";
try {
String sUidTmp = UserUtils.getUser().getId();
if (null != sUidTmp && sUidTmp.trim().length() > 0) {
sUid = sUidTmp.trim();
}
} catch (Exception ex) {
}
return sUid;
}
}
UploadNetStreamCallback类:
org.csource.fastdfs包为FastDFS包,需要自行下载引用。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.csource.fastdfs.UploadCallback;
public class UploadNetStreamCallback implements UploadCallback{
private InputStream is= null;
public InputStream getIs() {
return is;
}
public void setIs(InputStream is) {
this.is = is;
}
public UploadNetStreamCallback(InputStream is) {
super();
this.is = is;
}
/**
* send file content callback function, be called only once when the file uploaded
* @param out output stream for writing file content
* @return 0 success, return none zero(errno) if fail
*/
public int send(OutputStream out) throws IOException {
int readBytes;
byte[] buff = new byte[8192];
try {
while ((readBytes = this.is.read(buff)) >= 0) {
if (readBytes == 0) {
continue;
}
out.write(buff, 0, readBytes);
}
} catch (IOException ex) {
ex.printStackTrace();
return -1;
}
return 0;
}
}
UploadifyResult类:
public class UploadifyResult implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private String fileId;
private String fileName;
private String fileUrl;
private String preShowPath;//访问文件前缀
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileUrl() {
return fileUrl;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
public String getPreShowPath() {
return preShowPath;
}
public void setPreShowPath(String preShowPath) {
this.preShowPath = preShowPath;
}
}
FileUtils类:
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
/**
* 文件操作工具类
* 实现文件的创建、删除、复制、压缩、解压以及目录的创建、删除、复制、压缩解压等功能
* @author
* @version 2015-3-16
*/
public class FileUtils extends org.apache.commons.io.FileUtils {
private static Logger logger = LoggerFactory.getLogger(FileUtils.class);
/**
* 复制单个文件,如果目标文件存在,则不覆盖
* @param srcFileName 待复制的文件名
* @param descFileName 目标文件名
* @return 如果复制成功,则返回true,否则返回false
*/
public static boolean copyFile(String srcFileName, String descFileName) {
return FileUtils.copyFileCover(srcFileName, descFileName, false);
}
/**
* 复制整个目录的内容,如果目标目录存在,则不覆盖
* @param srcDirName 源目录名
* @param descDirName 目标目录名
* @return 如果复制成功返回true,否则返回false
*/
public static boolean copyDirectory(String srcDirName, String descDirName) {
return FileUtils.copyDirectoryCover(srcDirName, descDirName,
false);
}
/**
*
* 删除文件,可以删除单个文件或文件夹
*
* @param fileName 被删除的文件名
* @return 如果删除成功,则返回true,否是返回false
*/
public static boolean delFile(String fileName) {
File file = new File(fileName);
if (!file.exists()) {
logger.debug(fileName + " 文件不存在!");
return true;
} else {
if (file.isFile()) {
return FileUtils.deleteFile(fileName);
} else {
return FileUtils.deleteDirectory(fileName);
}
}
}
/**
*
* 删除单个文件
*
* @param fileName 被删除的文件名
* @return 如果删除成功,则返回true,否则返回false
*/
public static boolean deleteFile(String fileName) {
File file = new File(fileName);
if (file.exists() && file.isFile()) {
if (file.delete()) {
logger.debug("删除文件 " + fileName + " 成功!");
return true;
} else {
logger.debug("删除文件 " + fileName + " 失败!");
return false;
}
} else {
logger.debug(fileName + " 文件不存在!");
return true;
}
}
/**
*
* 删除目录及目录下的文件
*
* @param dirName 被删除的目录所在的文件路径
* @return 如果目录删除成功,则返回true,否则返回false
*/
public static boolean deleteDirectory(String dirName) {
String dirNames = dirName;
if (!dirNames.endsWith(File.separator)) {
dirNames = dirNames + File.separator;
}
File dirFile = new File(dirNames);
if (!dirFile.exists() || !dirFile.isDirectory()) {
logger.debug(dirNames + " 目录不存在!");
return true;
}
boolean flag = true;
// 列出全部文件及子目录
File[] files = dirFile.listFiles();
for (int i = 0; i < files.length; i++) {
// 删除子文件
if (files[i].isFile()) {
flag = FileUtils.deleteFile(files[i].getAbsolutePath());
// 如果删除文件失败,则退出循环
if (!flag) {
break;
}
}
// 删除子目录
else if (files[i].isDirectory()) {
flag = FileUtils.deleteDirectory(files[i]
.getAbsolutePath());
// 如果删除子目录失败,则退出循环
if (!flag) {
break;
}
}
}
if (!flag) {
logger.debug("删除目录失败!");
return false;
}
// 删除当前目录
if (dirFile.delete()) {
logger.debug("删除目录 " + dirName + " 成功!");
return true;
} else {
logger.debug("删除目录 " + dirName + " 失败!");
return false;
}
}
/**
* 创建单个文件
* @param descFileName 文件名,包含路径
* @return 如果创建成功,则返回true,否则返回false
*/
public static boolean createFile(String descFileName) {
File file = new File(descFileName);
if (file.exists()) {
logger.debug("文件 " + descFileName + " 已存在!");
return false;
}
if (descFileName.endsWith(File.separator)) {
logger.debug(descFileName + " 为目录,不能创建目录!");
return false;
}
if (!file.getParentFile().exists()) {
// 如果文件所在的目录不存在,则创建目录
if (!file.getParentFile().mkdirs()) {
logger.debug("创建文件所在的目录失败!");
return false;
}
}
// 创建文件
try {
if (file.createNewFile()) {
logger.debug(descFileName + " 文件创建成功!");
return true;
} else {
logger.debug(descFileName + " 文件创建失败!");
return false;
}
} catch (Exception e) {
e.printStackTrace();
logger.debug(descFileName + " 文件创建失败!");
return false;
}
}
/**
* 创建目录
* @param descDirName 目录名,包含路径
* @return 如果创建成功,则返回true,否则返回false
*/
public static boolean createDirectory(String descDirName) {
String descDirNames = descDirName;
if (!descDirNames.endsWith(File.separator)) {
descDirNames = descDirNames + File.separator;
}
File descDir = new File(descDirNames);
if (descDir.exists()) {
logger.debug("目录 " + descDirNames + " 已存在!");
return false;
}
// 创建目录
if (descDir.mkdirs()) {
logger.debug("目录 " + descDirNames + " 创建成功!");
return true;
} else {
logger.debug("目录 " + descDirNames + " 创建失败!");
return false;
}
}
/**
* 获取文件扩展名(返回小写)
* @param fileName 文件名
* @return 例如:test.jpg 返回: jpg
*/
public static String getFileExtension(String fileName) {
if ((fileName == null) || (fileName.lastIndexOf(".") == -1) || (fileName.lastIndexOf(".") == fileName.length() - 1)) {
return null;
}
return StringUtils.lowerCase(fileName.substring(fileName.lastIndexOf(".") + 1));
}
}
以上为个人观点,与大家共同学习共同提高。
推荐阅读
-
基于WebUploader、SpringMVC的断点续传
-
详解Spring框架之基于Restful风格实现的SpringMVC
-
【Spring in action】SpringMVC基于xml及java配置的简单运用
-
Spring in Action(五):基于Java Config的SpringMVC
-
基于WebUploader实现单图片和多图片上传,上传回显,编辑加载,图片删除,位置切换以及基于PhotoSwipe框架的图片预览功能
-
《基于注解的SpringMVC增删改DEMO源码》Maven版
-
基于SpringMvc实现Restful方式接口返回406的问题
-
基于WebUploader的图片上传解析
-
基于SpringMVC的全局异常处理器介绍
-
基于WebUploader的图片上传解析