ajax|js| Performance
First, the introduction
Browser-based file uploads, especially for uploading via the <input type= "file" tag, are also a serious performance problem. We know that uploading files over 10MB often leads to a very painful user experience. Once the user submits the file, the interface appears to be in a static state as the browser uploads the file to the server. Since all this happened in the background, many impatient users began to think that the server was "hung up", thus submitting the file again, which of course made the situation worse.
In order to make file uploads as comfortable as possible, once a user submits a file, many sites will display an intermediate process animation (such as a rotating icon). Although this technique has some effect when uploading to the server, it provides too little information about the file upload status. Another attempt to solve this problem is to implement a applet--that uploads files to the server via FTP. The downside of this scenario is that you have to have a Java-enabled browser that restricts your users.
In this article, we will implement an AJAX-capable component that not only implements the actual process of uploading files to the server, but also "real-time" monitoring of file uploads. The four phases of this component's work are shown in Figures 1,2,3 and 4 below:
Figure 1. Phase 1: Select File Upload
Figure 2. Phase 2: Uploading the file to the server
Figure 3. Phase 3: Upload complete
Figure 4. Phase 4: File Upload Summary |
Second, the implementation of the component
First, we analyze the process of creating a multiple-part filter that will allow us to process and monitor file uploads. We will then continue to implement the JavaServer Faces (JSF) component-it will provide continuous feedback to the user to support the Ajax progress bar approach.
(i) Multi-part filtration: Uploadmultipartfilter
The task of multi-part filtering is to intercept incoming file uploads and write the file to a temporary directory on a server. It also monitors the number of bytes received and determines the extent to which the file has been uploaded. Fortunately, there is now an excellent jakarta-commons Open Source Library available (FileUpload), which is responsible for parsing an HTTP multi-part Request and uploading the file to the server. What we need to do is expand the library and add the "hooks" we need to monitor how many bytes have been processed.
public class Uploadmultipartfilter implements filter{ public void Dofilter (ServletRequest request,servletresponse response,filterchain chain) Throws IOException, Servletexception { HttpServletRequest hrequest = (httpservletrequest) request; Check if we're dealing with a multi-part Request String Contentheader = Hrequest.getheader ("Content-type"); Boolean Ismultipart = (contentheader!= null && contentheader.indexof ("Multipart/form-data")!=-1); if (Ismultipart = = False) { Chain.dofilter (Request,response); }else{ Uploadmultipartrequestwrapper wrapper = new Uploadmultipartrequestwrapper (hrequest); Chain.dofilter (Wrapper,response); } ... } |
As you can see, the Uploadmultipartfilter class simply checks whether the current request is a multiple-part request. If the request does not include a file upload, the request is passed to the next filter in the request chain without any additional processing. Otherwise, the request will be wrapped in a uploadmultipartrequestwrapper.
(ii) Uploadmultipartrequestwrapper class
public class Uploadmultipartrequestwrapper Extends httpservletrequestwrapper{ Private Map <String,String> formparameters; Private Map <String,FileItem> fileparameters; Public Uploadmultipartrequestwrapper (HttpServletRequest request) { Super (Request); try{ Servletfileupload upload = new Servletfileupload (); Upload.setfileitemfactory (Request) (new progressmonitorfileitemfactory); List Fileitems = upload.parserequest (request); Formparameters = new HashMap <String,String> (); Fileparameters = new HashMap <String,FileItem> (); for (int i=0;i<fileitems.size (); i++) { Fileitem item = (fileitem) fileitems.get (i); if (Item.isformfield () = = True) { Formparameters.put (Item.getfieldname (), item.getstring ()); }else{ Fileparameters.put (Item.getfieldname (), item); Request.setattribute (Item.getfieldname (), item); } } }catch (fileuploadexception fe) { Request time Exceeded-The user may have moved to another page. Make some Records //... } ... |
In the Uploadmultipartrequestwrapper class, we will initialize the Servletfileupload class, which is responsible for parsing our request and writing the file to the default temp directory on the server. The Servletfileupload instance creates a Fileitem instance for each field encountered in the request (they contain file uploads and normal form elements). A Fileitem instance is then used to retrieve the properties of a submitted field, or, in the case of a file upload, to retrieve a inputstream to the underlying temporary file. In summary, Uploadmultipartrequestwrapper is responsible for parsing the file and setting any fileitem-it describes the file upload as an attribute in the request. These properties are then further collected by the JSF component, and the behavior of normal form fields remains unchanged.
By default, the generic FileUpload library uses an instance of the Diskfileitems class to process file uploads. Although Diskfileitem is useful for handling the entire temporary file business, there is little support for accurate monitoring of how well the file has been handled. Since version 1.1, the Universal FileUpload Library has enabled developers to specify the factories that are used to create fileitem. We will use the Progressmonitorfileitemfactory and Progressmonitorfileitem classes to overload the default behavior and monitor the file upload process.
(iii) Progressmonitorfileitemfactory class
public class Progressmonitorfileitemfactory extends Diskfileitemfactory { Private File temporarydirectory; Private HttpServletRequest Requestref; Private long requestlength; Public progressmonitorfileitemfactory (HttpServletRequest request) { Super (); Temporarydirectory = (File) request.getsession (). Getservletcontext (). getattribute ("Javax.servlet.context.tempdir" ); Requestref = Request; String contentlength = Request.getheader ("Content-length"); if (contentlength!= null) {requestlength = Long.parselong (Contentlength.trim ());} } Public Fileitem CreateItem (string fieldName, String Contenttype,boolean Isformfield, string fileName) { Sessionupdatingprogressobserver observer = NULL; if (Isformfield = false)//This must be a file upload. Observer = new Sessionupdatingprogressobserver (fieldname,filename); Progressmonitorfileitem item = new Progressmonitorfileitem ( Fieldname,contenttype,isformfield, Filename,2048,temporarydirectory, Observer,requestlength); return item; } ... public class Sessionupdatingprogressobserver implements Progressobserver { Private String FieldName; Private String FileName; ... public void setprogress (double progress) { if (request!= null) { Request.getsession (). setattribute ("fileupload.progress.") +fieldname,progress); Request.getsession (). setattribute ("Fileupload.filename.") +fieldname,filename); } } } } |
The Progressmonitorfileitemfactory content-length header is set by the browser and is assumed to be the exact length of the uploaded file being set. This method of determining file lengths does limit the files that you upload in each request-if multiple files are encoded in the request, this value is inaccurate. This is because browsers only send a content-length header, regardless of the number of files uploaded.
In addition to creating a Progressmonitorfileitem instance, Progressmonitorfileitemfactory also registers a progressobserver instance. It will be sent by Progressmonitorfileitem to update the file during the upload process. The implementation of the progressobserver that we are using (Sessionupdatingprogressobserver) Sets the progress percentage to the user's session for the ID of the submitted field. This value can then be accessed by the JSF component to send the update to the user.