ajax 异步上传带进度条视频并提取缩略图

最近在做一个集富媒体功能于一身的项目。需要上传视频。这里我希望做成异步上传,并且有进度条,响应有状态码,视频连接,缩略图。

服务端响应

 {
   "thumbnail": "/slsxpt//upload/thumbnail/fdceefc.jpg",
   "success": true,
   "link": "/slsxpt//upload/video/fdceefc.mp"
 }

并且希望我的input file控件不要被form标签包裹。原因是form中不能嵌套form,另外form标签在浏览器了还是有一点点默认样式的,搞不好又要写css。

以前用ajaxFileUpload做过文件异步上传。不过这个东西好久未更新,代码还有bug,虽然最后勉强成功用上了,但总觉不好。而且ajaxFileUpload没有直接添加xhr2的progress事件响应,比较麻烦。

上网找了一下,发现方法都是很多。

比如在文件上传后,将上传进度放到session中,轮询服务器session。但我总觉的这个方法有问题,我认为这种方法看到的进度,应该是我的服务端应用程序代码(我的也就是action)从服务器的临时目录复制文件的进度,因为所有请求都应该先提交给服务器软件,也就是tomcat,tomcat对请求进行封装session,request等对象,并且文件实际上也应该是它来接收的。也就是说在我的action代码执行之前,文件实际上已经上传完毕了。

后来找到个比较好的方法使用 jquery.form.js插件的ajaxSubmit方法。这个方法以表单来提交,也就是 $.fn.ajaxSubmit.:$(form selector).ajaxSubmit({}),这个api的好处是它已经对xhr2的progress时间进行了处理,可以在调用时传递一个uploadProgress的function,在function里就能够拿到进度。而且如果不想input file被form包裹也没关系,在代码里createElement应该可以。不过这个方法我因为犯了个小错误最后没有成功,可惜了。

ajaxSubmit源码

最后,还是使用了$.ajax 方法来做。$.ajax 不需要关联form,有点像个静态方法哦。唯一的遗憾就是$.ajax options里没有对progress的响应。不过它有一个参数为 xhr ,也就是你可以定制xhr,那么久可以通过xhr添加progress的事件处理程序。再结合看一看ajaxSubmit方法里对progress事件的处理,顿时豁然开朗

那么我也可以在$.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),
       var  formData = 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运算,应该可以稍微避免下重复上传相同文件了。

转码的时候用到FFmpeg。需要的可以去这里下载。

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

(0)

相关推荐

  • ajax图片上传,图片异步上传,更新实例

    最近在研究ajax图片上传,图片异步上传,更新,留作参考. 直接上源码吧: js源码: /// <reference path="jquery-1.8.3.js" /> /// <reference path="ajaxForm/jquery.form.js" /> /*! * jQuery upload * 用于上传单个文件,上传成功后,返回文件路径. * 本插件依赖jQuery,jquery.form 请在使用时引入依赖的文件 * * D

  • jQuery插件ajaxFileUpload实现异步上传文件效果

    ajaxFileUpload是一个异步上传文件的jQuery插件. 传一个不知道什么版本的上来,以后不用到处找了. 语法:$.ajaxFileUpload([options]) options参数说明: 1.url   上传处理程序地址. 2,fileElementId   需要上传的文件域的ID,即<input type="file">的ID. 3,secureuri 是否启用安全提交,默认为false. 4,dataType 服务器返回的数据类型.可以为xml,scri

  • jquery的ajaxSubmit()异步上传图片并保存表单数据演示代码

    (jsp需要引入 :jquery-1.9.0.js.jquery.form.js ) ,jsp页面使用的是bootstrap制作的,看不懂的标签不用管,form表单大同小异.代码比较简陋,只是为了演示使用ajaxSubmit异步上传图片及保存数据,请海含! (参考文献:http://www.jb51.net/shouce/jquery/jquery_api/Plugins/Form/ajaxSubmit.html) 一:web (add.jsp) 复制代码 代码如下: <%@page impor

  • jQuery插件ajaxFileUpload异步上传文件

    ajaxFileUpload.js 很多同名的,因为做出来一个很容易. 我用的是这个:https://github.com/carlcarl/AjaxFileUpload 下载地址在这里:http://xiazai.jb51.net/201610/yuanma/ajaxfileupload(jb51.net).rar AjaxFileUpload.js并不是一个很出名的插件,只是别人写好的放出来供大家用,原理都是创建隐藏的表单和iframe然后用JS去提交,获得返回值. 当初做了个异步上传的功能

  • jQuery异步上传文件插件ajaxFileUpload详细介绍

    一.ajaxFileUpload是一个异步上传文件的jQuery插件. 传一个不知道什么版本的上来,以后不用到处找了. 语法:$.ajaxFileUpload([options]) options参数说明: 1.url   上传处理程序地址. 2,fileElementId   需要上传的文件域的ID,即<input type="file">的ID. 3,secureuri 是否启用安全提交,默认为false. 4,dataType 服务器返回的数据类型.可以为xml,sc

  • JQuery插件ajaxfileupload.js异步上传文件实例

    在服务器端做文件上传的过程中,如果使用web服务器短端的上传控件去上传文件的话,会导致页面刷新一次,这样对用户的体验就不是很友好了.ajaxfileupload.js是一款jQuery的异步上传文件插件,使用简单且容易上手. 前置条件:ajaxfileupload.js文件,百度下载一个就行. JS引用: 复制代码 代码如下: <script src="/Content/JQueryJS/jquery-2.1.1.js"></script> <script

  • jquery之ajaxfileupload异步上传插件(附工程代码)

    点我下载工程代码 由于项目需求,在处理文件上传时需要使用到文件的异步上传.这里使用Jquery Ajax File Uploader这个组件下载地址:http://www.phpletter.com/download_project_version.php?version_id=6 服务器端采用struts2来处理文件上传. 所需环境: jquery.js ajaxfileupload.js struts2所依赖的jar包 及struts2-json-plugin-2.1.8.1.jar 编写文

  • PHP结合jQuery插件ajaxFileUpload实现异步上传文件实例

    平时用的比较多的JQuery图片上传插件是Uploadify这个插件,效果很不错,但是由于手机不支持flash,所以不得不再找一个文件上传插件来用了.后来发现ajaxFileUpload这个插件挺不错,所以就用这个插件来做异步上传文件的效果.网上也有很多对ajaxFileUpload插件的使用的文章,不过我发现没有PHP版,所以这次服务器那边的处理就使用PHP语言来处理. 一.详解ajaxFileUpload插件的语法参数 原理:ajaxfileupload是通过监听iframe的onload方

  • asp.net+ajaxfileupload.js 实现文件异步上传代码分享

    由于代码很简单,这里就闲话不多说了,直接上代码,小伙伴们自己研读代码就明白了. 前台代码: 复制代码 代码如下: /*修改头像*/      //上传      function _sc() {          $(".ckfile").html("").css("color", "#535353");          $("#_userImgPath").val("");     

  • php+ajax实现异步上传文件或图片功能

    本文为大家分享了ajax异步上传文件或图片功能的具体代码,供大家参考,具体内容如下 //html代码 <form enctype="multipart/form-data" id="upForm"> <input type="file" name="file" ><br><br> <input type="button" value="提交&q

随机推荐