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

时间: 2024-12-10 21:05:18

ajax 异步上传带进度条视频并提取缩略图的相关文章

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

最近在做一个集富媒体功能于一身的项目.需要上传视频.这里我希望做成异步上传,并且有进度条,响应有状态码,视频连接,缩略图. 服务端响应 { "thumbnail": "/slsxpt//upload/thumbnail/fdceefc.jpg", "success": true, "link": "/slsxpt//upload/video/fdceefc.mp" } 并且希望我的input file控件不

jQuery多文件异步上传带进度条实例代码_jquery

先给大家展示下效果图: ///作者:柯锦 ///完成时间:2016.08.16 ///多文件异步上传带进度条 (function ($) { function bytesToSize(bytes) { if (bytes === 0) return '0 B'; var k = 1024, // or 1000 sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], i = Math.floor(Math.log(bytes)

基于Ajax技术实现文件上传带进度条_AJAX相关

1.概述 在实际的Web应该开发或网站开发过程中,经常需要实现文件上传的功能.在文件上传过程中,经常需要用户进行长时间的等待,为了让用户及时了解上传进度,可以在上传文件的同时,显示文件的上传进度条.运行本实例,如图1所示,访问文件上传页面,单击"浏览"按钮选择要上传的文件,注意文件不能超过50MB,否则系统将给出错误提示.选择完要上传的文件后,单击"提交"按钮,将会上传文件并显示上传进度. 2.技术要点 主要是应用开源的Common-FileUpload组件来实现分

基于Ajax技术实现文件上传带进度条

1.概述 在实际的Web应该开发或网站开发过程中,经常需要实现文件上传的功能.在文件上传过程中,经常需要用户进行长时间的等待,为了让用户及时了解上传进度,可以在上传文件的同时,显示文件的上传进度条.运行本实例,如图1所示,访问文件上传页面,单击"浏览"按钮选择要上传的文件,注意文件不能超过50MB,否则系统将给出错误提示.选择完要上传的文件后,单击"提交"按钮,将会上传文件并显示上传进度. 2.技术要点 主要是应用开源的Common-FileUpload组件来实现分

Asp无组件上传带进度条(续)

上传|无组件 几个月前,写过一篇关于Asp无组件上传带进度条的Blog,当时主要分析了一下通过Web文件上传并在后台获取分析数据的实现原理.其中实现进度条的根本就是要实现分块获取数据,根据分块大小和块数记录已上传大小.还有一些具体的细节问题当时并没有说清楚: Q: 进度信息如何访问?A: 进度信息保存在Application中,每次上传时生成一个进度ID,根据这个进度ID可以检索Application中的当前上传进度信息. Q: 进度信息以什么形式保存在Application中?A: Asp太弱

Jquery Uploadify上传带进度条介绍

 本篇文章主要是对Jquery Uploadify上传带进度条的简单实例进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助  代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="UpLoad.aspx.cs" Inherits="UploadifyDemo_UpLoad" %>   <html xmlns="http://

Jquery Uploadify上传带进度条的简单实例_jquery

复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="UpLoad.aspx.cs" Inherits="UploadifyDemo_UpLoad" %> <html xmlns="http://www.w3.org/1999/xhtml"><head id="Head1" ru

Jquery Uploadify多文件上传带进度条且传递自己的参数_jquery

复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="UpLoad.aspx.cs" Inherits="UploadifyDemo_UpLoad" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" r

asp.net文件上传带进度条实现案例(多种风格)_实用技巧

先饱饱眼福: 在之前的文章中也有类似带进度条文件传送的案例,大家可以翻阅之前的文章对知识点进行扩充. 部分代码: <%@ Page Language="C#" %> <%@ Register Assembly="MattBerseth.WebControls.AJAX" Namespace="MattBerseth.WebControls.AJAX.Progress" TagPrefix="mb" %>