Ajax uploads a video with a progress bar asynchronously and extracts thumbnails. ajax progress bar

Source: Internet
Author: User

Ajax uploads a video with a progress bar asynchronously and extracts thumbnails. ajax progress bar

Recently, I am working on a project that integrates rich media functions. You need to upload a video. Here, I want to make asynchronous uploads with progress bars, response status codes, video connections, and thumbnails.

Server Response

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

I also hope that my input file control will not be wrapped by the form label. The reason is that the form cannot be nested in the form. In addition, the form label still has a little default style in the browser, and css should be written if it is difficult to do so.

Previously, files were asynchronously uploaded using ajaxFileUpload. However, this item has not been updated for a long time, and there are bugs in the Code. Although it was barely used in the end, it is always difficult. In addition, ajaxFileUpload does not directly add the xhr2 progress event response, which is troublesome.

I searched the internet and found many methods.

For example, after a file is uploaded, put the upload progress in the session and poll the server session. But I always think there is a problem with this method. I think the progress of this method should be my server application code (my action) copy the File progress from the temporary directory of the server, because all requests should be submitted to the server software, that is, tomcat. tomcat encapsulates the request session, request, and other objects, and the file should actually be received by it. That is to say, before my action code is executed, the file has actually been uploaded.

Later, I found a better method to use the ajaxSubmit METHOD OF THE jquery. form. js plug-in. This method is submitted in a form, that is, $. fn. ajaxSubmit.: $ (form selector ). ajaxSubmit ({}). The advantage of this api is that it has processed the progress time of xhr2. You can pass an uploadProgress function during the call to get the progress in the function. It doesn't matter if you don't want the input file to be wrapped by form. You can use createElement in the code. However, it is a pity that I failed to make a small mistake in this method.

Source code of ajaxSubmit

Finally, the $. ajax method is used. $. Ajax does not need to be associated with form. It is a bit like a static method. The only pity is that $. ajax options does not respond to progress. However, it has a parameter xhr, that is, you can customize xhr, so you can add a progress Event Handler through xhr for a long time. Then let's take a look at the processing of the progress SS event in the ajaxSubmit method.

Then I can add the progress event processing function in the $. ajax method. To extract dom operations from the upload service, I decided to write them in the form of plug-ins. The following is the code of the plug-in.

; (Function ($) {var defaults = {uploadProgress: null, beforeSend: null, success: null,}, setting = {}; var upload = function ($ this) {$ this. parent (). on ('change', $ this, function (event) {// var $ this = condition (event.tar get), var formData = new FormData (), target = event.tar get | 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, ype: '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 for file upload. each (function () {upload ($ (this) ;}}) ($ | jQuery );

Now you can use this api in my jsp page.

<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-success uploadVideoProgress" role = "progressbar" aria-valuenow = "" aria-valuemin = "" aria-valuemax = "" style = "width: % "> </div> <input type =" file "class =" form-control file inline btn-primary uploadInput uploadVideo "accept =" video/mp" data-url = "$ {baseUrl}/upload-video.action" data-label = "<I class = 'glyphicon glyphicon-circle-arrow-up'> </I> select a file "/> <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>

In response to succes, the setting times out 800 milliseconds to get the image, because the extraction of the scaling chart is another process that does not extract the thumbnail when the response is completed.

View the effect

Extract scaling chart

The following section describes how the server processes uploads and scales down the video extraction. The following figure shows the action processing code.

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 Uplo AdAction 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; // omitting the setter getter method public String video () {Map <String, Object> dataJson = new HashMap <String, Object> (); System. out. println (file); System. out. println (fi LeFileName); 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; // extract the scale chart needThum B = 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 ("thumbnail extracted successfully:" + 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 Configuration

 <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>

I personally think that if the names and sizes of a file are the same, the probability that they are a file is very high. Therefore, I will calculate the file name and size for md5, you can avoid uploading the same file repeatedly.

FFmpeg is used for transcoding. You can download it here.

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 extract Thumbnail (File inputFile, String thumbnailOutput) {List <String> command = new ArrayList <String> (); File ffmpegExe = new File (FFMPEG_EXECUTOR); if (! FfmpegExe. exists () {System. out. println ("transcoding tool does not exist"); 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 ("startup time consumed" + (System. currentTimeMillis ()-startTime); return true;} catch (IOException e) {e. printStackTrace (); return false ;}}}

In addition, Java starts another process. In my opinion, they should be non-coherent. After Java starts ffmpeg.exe, it should be back to execute the following code, so there is no need to start a separate thread to extract the scaling diagram. The test also found that the time consumption was not long. The time consumed for each long upload is not much different. The following figure shows the time consumed for two uploads to the same file.

First time

Second

There is no big difference in user experience.

In addition, you need to configure tomcat and struct to upload large files.

Modify the server. xml file in the conf directory of tomcat and add maxPostSize = "0" to the Connector node to indicate that the upload size is not displayed.

In addition, modify struts. xml to add the configuration. The value here is measured in bytes, which is about 300 mb

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.