{ "thumbnail": "/slsxpt//upload/thumbnail/fdceefc.jpg", "success": true, "link": "/slsxpt//upload/video/fdceefc.mp" }并且希望我的input file控件不要被form标签包裹。原因是form中不能嵌套form,另外form标签在浏览器了还是有一点点默认样式的,搞不好又要写css。
那么我也可以在$.ajax 方法中添加progress事件处理函数了。为了把对dom的操作从上传业务中抽取出来,我决定以插件的形式写。下面是插件的代码
;(function ($) { var defaults = { uploadProgress:null, beforeSend:null, success:null, }, setting = { }; var upload = function($this){ $this.parent().on("change",$this,function(event){ //var $this = $(event.target), varformData = new FormData(), target = event.target || event.srcElement; //$.each(target.files, function(key, value) //{ //console.log(key); //formData.append(key, value); //}); formData.append("file",target.files[]); settings.fileType && formData.append("fileType",settings.fileType); $.ajax({ url:$this.data("url"), type:"POST", data:formData, dataType:"json", processData:false, contentType:false, cache:false, beforeSend:function(){ //console.log("start"); if(settings.beforeSend){ settings.beforeSend(); } }, xhr: function() { var xhr = $.ajaxSettings.xhr(); if(xhr.upload){ xhr.upload.addEventListener("progress",function(event){ var total = event.total, position = event.loaded || event.position, percent = ; if(event.lengthComputable){ percent = Math.ceil(position / total * ); } if(settings.uploadProgress){ settings.uploadProgress(event, position, total, percent); } }, false); } return xhr; }, success:function(data,status,jXhr){ if(settings.success){ settings.success(data); } }, error:function(jXhr,status,error){ if(settings.error){ settings.error(jXhr,status,error); } } }); }); }; $.fn.uploadFile = function (options) { settings = $.extend({}, defaults, options); // 文件上传 return this.each(function(){ upload($(this)); }); } })($ || jQuery);下面就可以在我的jsp页面里面使用这个api了。
<div class="col-sm-"> <input type="text" name="resource_url" id="resource_url" hidden="hidden"/> <div class="progress" style="display: none;"> <div class="progress-bar progress-bar-success uploadVideoProgress" role="progressbar"aria-valuenow="" aria-valuemin="" aria-valuemax="" style="width: %"> </div> </div> <input type="file" class="form-control file inline btn btn-primary uploadInput uploadVideo"accept="video/mp"data-url="${baseUrl}/upload-video.action"data-label="<i class="glyphicon glyphicon-circle-arrow-up"></i> 选择文件" /> <script> (function($){ $(document).ready(function(){ var $progress= $(".uploadVideoProgress"), start = false; $("input.uploadInput.uploadVideo").uploadFile({ beforeSend : function(){ $progress.parent().show(); }, uploadProgress : function(event, position, total, percent){ $progress.attr("aria-valuenow",percent); $progress.width(percent+"%"); if(percent >= ){ $progress.parent().hide(); $progress.attr("aria-valuenow",); $progress.width(+"%"); } }, success : function(data){ if(data.success){ setTimeout(function(){ $("#thumbnail").attr("src",data.thumbnail); },); } } }); }); })(jQuery); </script> </div>这里在响应succes的时候设置超时800毫秒之后获取图片,因为提取缩量图是另一个进程在做可能响应完成的时候缩略图还没提取完成
提取缩量图
下面部分就是服务端处理上传,并且对视频提取缩量图下面是action的处理代码
package org.lyh.app.actions; import org.apache.commons.io.FileUtils; import org.apache.struts.ServletActionContext; import org.lyh.app.base.BaseAction; import org.lyh.library.SiteHelpers; import org.lyh.library.VideoUtils; import java.io.File; import java.io.IOException; import java.security.KeyStore; import java.util.HashMap; import java.util.Map; /** * Created by admin on //. */ public class UploadAction extends BaseAction{ private String saveBasePath; private String imagePath; private String videoPath; private String audioPath; private String thumbnailPath; private File file; private String fileFileName; private String fileContentType; // 省略setter getter方法 public String video() { Map<String, Object> dataJson = new HashMap<String, Object>(); System.out.println(file); System.out.println(fileFileName); System.out.println(fileContentType); String fileExtend = fileFileName.substring(fileFileName.lastIndexOf(".")); String newFileName = SiteHelpers.md(fileFileName + file.getTotalSpace()); String typeDir = "normal"; String thumbnailName = null,thumbnailFile = null; boolean needThumb = false,extractOk = false; if (fileContentType.contains("video")) { typeDir = videoPath; // 提取缩量图 needThumb = true; thumbnailName = newFileName + ".jpg"; thumbnailFile = app.getRealPath(saveBasePath + thumbnailPath) + "/" + thumbnailName; } String realPath = app.getRealPath(saveBasePath + typeDir); File saveFile = new File(realPath, newFileName + fileExtend); // 存在同名文件,跳过 if (!saveFile.exists()) { if (!saveFile.getParentFile().exists()) { saveFile.getParentFile().mkdirs(); } try { FileUtils.copyFile(file, saveFile); if(needThumb){ extractOk = VideoUtils.extractThumbnail(saveFile, thumbnailFile); System.out.println("提取缩略图成功:"+extractOk); } dataJson.put("success", true); } catch (IOException e) { System.out.println(e.getMessage()); dataJson.put("success", false); } }else{ dataJson.put("success", true); } if((Boolean)dataJson.get("success")){ dataJson.put("link", app.getContextPath() + "/" + saveBasePath + typeDir + "/" + newFileName + fileExtend); if(needThumb){ dataJson.put("thumbnail", app.getContextPath() + "/" + saveBasePath + thumbnailPath + "/" + thumbnailName); } } this.responceJson(dataJson); return NONE; } }action配置
<action name="upload-*" class="uploadAction" method="{}"> <param name="saveBasePath">/upload</param> <param name="imagePath">/images</param> <param name="videoPath">/video</param> <param name="audioPath">/audio</param> <param name="thumbnailPath">/thumbnail</param> </action>这里个人认为,如果文件的名称跟大小完全一样的话,它们是一个文件的概率就非常大了,所以我这里取文件名跟文件大小做md5运算,应该可以稍微避免下重复上传相同文件了。
package org.lyh.library; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * Created by admin on //. */ public class VideoUtils { public static final String FFMPEG_EXECUTOR = "C:/Software/ffmpeg.exe"; public static final int THUMBNAIL_WIDTH = ; public static final int THUMBNAIL_HEIGHT = ; public static boolean extractThumbnail(File inputFile,String thumbnailOutput){ List<String> command = new ArrayList<String>(); File ffmpegExe = new File(FFMPEG_EXECUTOR); if(!ffmpegExe.exists()){ System.out.println("转码工具不存在"); return false; } System.out.println(ffmpegExe.getAbsolutePath()); System.out.println(inputFile.getAbsolutePath()); command.add(ffmpegExe.getAbsolutePath()); command.add("-i"); command.add(inputFile.getAbsolutePath()); command.add("-y"); command.add("-f"); command.add("image"); command.add("-ss"); command.add(""); command.add("-t"); command.add("."); command.add("-s"); command.add(THUMBNAIL_WIDTH+"*"+THUMBNAIL_HEIGHT); command.add(thumbnailOutput); ProcessBuilder builder = new ProcessBuilder(); builder.command(command); builder.redirectErrorStream(true); try { long startTime = System.currentTimeMillis(); Process process = builder.start(); System.out.println("启动耗时"+(System.currentTimeMillis()-startTime)); return true; } catch (IOException e) { e.printStackTrace(); return false; } } }另外这里由java启动了另外一个进程,在我看来他们应该是互不相干的,java启动了ffmpeg.exe之后,应该回来继续执行下面的代码,所以并不需要单独起一个线程去提取缩量图。测试看也发现耗时不多。每次长传耗时也区别不大,下面是两次上传同一个文件耗时
第二次
就用户体验来说没有很大的区别。
另外这里上传较大文件需要对tomcat和struct做点配置
修改tomcat下conf目录下的server.xml文件,为Connector节点添加属性 maxPostSize="0"表示不显示上传大小
另外修改 struts.xml添加配置,这里的value单位为字节,这里大概300多mb