Web browsers provide a simple way for us to use web applications to send files, but the current version of Java Web standards (Servlets, JSP, and JSF) cannot provide us with any help. Fortunately, some third-party framework components (such as Apache commons file upload, Apache MyFaces, and Oracle ADF faces) implement this feature, they all expose simple APIs and custom tags ).
The previous section describes how to implement the file upload operation and explains MyFaces and general File Upload (the former uses the latter internally ). We understand that the internal working methods of these open source framework components are very important, because only in this way can we use them efficiently and modify them as needed. The second part of this article will introduce a simple application that allows users to upload files using a web browser.
Web-based File Upload
We have used the term "Upload" too frequently. When a web administrator publishes a file on his or her website, he or she will say that he has uploaded a file. When creating html forms and scripts for common users to send a file through a Web browser, Web developers also say they have implemented the file "Upload" function.
These two meanings overlap in some aspects, because Web administrators may use web-based interfaces to publish files (pages, images, scripts, and so on ). Companies that provide personal websites (such as Yahoo) for free can upload Web files to allow people to upload their own pages. It allows anyone with Web browsers and Internet access to publish a small web site. However, there are some better ways to publish web content, such as ftp or secure FTP. In this case, you can use a dedicated application (such as an FTP client) to upload your content to the Web server instead of the Web browser.
This article discusses file upload from the perspective of Web developers. For example, a Web-based email application (such as Yahoo Mail) uploads files, because only in this way can users send messages with attachments. Another good example is the job-seeking web site, which allows users to send their resumes to recruiters. The example application in this article calculates the hash value of the uploaded file. You can process uploaded files in your own application, such as storing their content in the database or sending them as email attachments. Now, let's see how to upload files in Web applications.
An HTML form can contain one or more <input type = "file"> elements, which are displayed (rendered) as input fields by the browser ), you can enter the file path in these fields. After each input field of a file, a button is added to the web browser, which opens a file dialog box and allows users to select a file (instead of the input path ):
Figure 1: Web form containing file input fields |
When you click the "Submit" button of the form, the Web browser encodes the form data, including the selected file, file name (or path) and other parameters of the form. Then, the browser sends the encoded data to the Web server, which passes the data to the script specified by the Action attribute marked by <form>. If you develop a Java Web application, the script may be a servlet or JSP page.
Because the default encoding method and the default get method of form data are not suitable for file upload, therefore, the multipart/form-data encoding method and post method must be specified in the <form> mark for the form containing the input field of the file:
<Form enctype = "multipart/form-Data" method = "Post" Action = "..."> ... <Input type = "file" name = "..."> ... </Form> |
But it is not that simple, because the application server that implements Servlet and JSP specifications may not be able to process the multipart/form-data encoding method. Therefore, you must analyze the request input stream. For example, Apache universal File Upload is a small Java package that allows you to obtain the content of the uploaded file from the encoded data. The API of this package is flexible. It allows you to store small files in the memory and large files in the temporary directory of the disk. You can specify the threshold value for a file. All files larger than this value will be written to the disk and will not be kept in the memory, you can also specify the maximum size of the files to be uploaded.
Org. apache. commons. the fileupload package contains a class called diskfileupload. Its parserequest () method obtains the httpservletrequest parameter and returns Org. apache. commons. fileupload. list of fileitem instances. The encoded form data is read from the data stream returned by the getinputstream () method of the Servlet request. The fileitem name is easy to misunderstand, because the interface instance simultaneously shows the uploaded file and normal request parameters.
The API provided by the general file upload package grants you the right to access the decomposed form data, but the getparameter () and getparametervalues () Methods of servlet requests cannot work. This is a problem, because the JSF components running in the input field, check box, single-sheet, and list box background need to call these two methods. We can use the two features provided by the Servlets API (filter and request wrappers) to solve this problem. The next section describes how Apache MyFaces implements filters and adds a large number of necessary support for file uploads without interrupting the existing JSF components. In addition, MyFaces provides APIs for JavaBeans and a customized JSF component, which is represented by the <input type = "file"> element.
Configure JSF and MyFaces extensions
Currently, the main implementation of the JSF specification is the JSF reference implementation (RI). At the same time, Apache provides another implementation, namely MyFaces. Of course there may be some other JSF implementations, but jsf ri and MyFaces are two of the most popular ones. Many developers prefer the former because Sun calls it an "official" implementation, but MyFaces has some interesting extensions (such as support for file upload operations ). If you want to, you can use the MyFaces extension with Sun's JSF Ri. You just need to put the myfaces-extensions.jar file, the JAR file of JSF Ri, And the commons-fileupload-1.0.jar file together in your web application's WEB-INF/lib directory. The following is the required JAR file:
JSF 1.1 Ri jsf-api.jarjsf-impl.jar Jstl 1.1 Ri jstl. jarstandard. Jar MyFaces extension myfaces-extensions.jar Apache commons (for JSF and MyFaces extensions) commons-collections.jarcommons-digester.jarcommons-beanutils.jarcommons-logging.jarcommons-fileupload-1.0.jar |
The multipartrequestwrapper class in the org.apache.myfaces.component.html. util package serves as a bridge between MyFaces and general file uploads. This class extends httpservletrequestwrapper and reloads the getparametermap (), getparameternames (), getparameter (), and getparametervalues () Methods to adapt to the multipart/form-data encoding method. In addition, multipartrequestwrapper provides two new methods: getfileitem () and getfileitems (), which allow you to use Org. apache. commons. fileupload. fileitem interface to access uploaded files.
When myfacesdetects the signature, extensionsfilter in the org.apache.myfaces.component.html. util Package creates a multipartrequestwrapper instance. Therefore, you do not need to worry about the data decomposition of the form. But if you want to change the processing method of the uploaded file, it is very useful to know how it is implemented. In a typical application, you must configure extensionsfilter in the web. XML description of your web application so that it can intercept HTTP requests before the facesservlet of JSF:
<? XML version = "1.0" encoding = "UTF-8"?> <! Doctype web-app public "-// Sun Microsystems, Inc. // DTD web application 2.3 // en" Http://java.sun.com/dtd/web-app_2_3.dtd> <Web-app> <Context-param> <Param-Name> javax. Faces. state_saving_method </param-Name> <Param-value> client </param-value> </Context-param> <Servlet> <Servlet-Name> facesservlet </servlet-Name> <Servlet-class> Javax. Faces. webapp. facesservlet </Servlet-class> <Load-on-startup> 1 </load-on-startup> </Servlet> <Servlet-mapping> <Servlet-Name> facesservlet </servlet-Name> <URL-pattern>/faces/* </url-pattern> </Servlet-mapping> <Servlet-mapping> <Servlet-Name> facesservlet </servlet-Name> <URL-pattern> *. Faces </url-pattern> </Servlet-mapping> <Filter> <Filter-Name> extensionsfilter </filter-Name> <Filter-class> Org.apache.myfaces.component.html. util. extensionsfilter </Filter-class> <Init-param> <Param-Name> uploadmaxfilesize </param-Name> <Param-value> 10 m </param-value> </Init-param> <Init-param> <Param-Name> uploadthresholdsize </param-Name> <Param-value> 100 k </param-value> </Init-param> </Filter> <Filter-mapping> <Filter-Name> extensionsfilter </filter-Name> <Servlet-Name> facesservlet </servlet-Name> </Filter-mapping> <Welcome-file-List> <Welcome-File> index. jsp </welcome-File> </Welcome-file-List> </Web-app> |
The two filter parameters in the preceding example tell MyFaces to put files smaller than kb into the memory and ignore (that is, do not process) files that occupy more than 10 MB of disk space. Files Between uploadthresholdsize and uploadmaxfilesize are stored as temporary files on the disk. If you try to load a large file, the current version of MyFaces ignores all form data, just as if the user submitted an empty form. If you want to give a prompt to the user who fails to upload the file, you need to change the multipartrequestwrapper class of MyFaces, locate the location where the sizelimitexceededexception exception is captured, and use facescontext. getcurrentinstance (). addmessage () to warn users.
As mentioned above, the MyFaces extension contains the File Upload Component, which can be used on the JSF page. The next section describes how to implement this operation. Use the MyFaces File Upload Component.
To use JSF and MyFaces on a web page, you must declare their tag libraries in the <% @ taglib %> command:
<% @ Taglib uri = "http://java.sun.com/jsf/core" prefix = "F" %> <% @ Taglib uri = "http://java.sun.com/jsf/html" prefix = "H" %> <% @ Taglib uri = "http://myfaces.apache.org/extensions" prefix = "X" %> |
The
<F: View> <H: Form ID = "myform" enctype = "multipart/form-Data"> ... <X: inputfileupload id = "myfileid" Value = "# {mybean. myfile }" Storage = "file" Required = "true"/> ... </H: Form> </F: View> |
The <X: inputfileupload> flag of MyFaces enables you to define the attributes of the UI component. Its Rendering component (Renderer) generates the <input type = "file"> element. Org. apache. myFaces. custom. the fileupload package contains the UI component class (htmlinputfileupload), rendering component (htmlfileuploadrenderer), custom tag Processing Program (htmlinputfileuploadtag), uploadedfile interface, and other auxiliary classes. The htmlinputfileupload class extends the JSF standard htmlinputtext component and reloads some of its methods. Htmlfileuploadrenderer is responsible for generating HTML tags and obtaining fileitem from multipartrequestwrapper.
MyFaces does not allow you to directly access the fileitem instance created by uploading a common file. It provides its own uploadedfile interface to obtain the content, content type, file name, and file size of the uploaded file. The backend Bean Of The JSF form must have an uploadedfile attribute. The preceding example uses the JSF expression # {mybean. myfile} to bind the value of the UI component to such a bean. The JSF framework component gets the value of the htmlinputfileupload component. It is an uploadedfile instance and sets the myfile attribute of the backend Bean:
Import org. Apache. MyFaces. Custom. fileupload. uploadedfile; ... Public class mybean { Private uploadedfile myfile;Public uploadedfile getmyfile (){ Return myfile; } Public void setmyfile (uploadedfile myfile ){ This. myfile = myfile; } ... } |
MyFaces has two implementation methods for the uploadedfile interface: uploadedfiledefamemmemoryimpl and uploadedfiledefaultfileimpl. When the <X: inputfileupload> flag does not contain the storage attribute or the value of this attribute is memory, use the former. The latter is used when the storage attribute value is file.
The uploadedfiledefamemmemoryimpl class obtains the content, file name, file size, and content type of the uploaded file from the fileitem instance, and stores all the information in private fields. Therefore, even if the general file is uploaded and the file is stored on the disk, the uploadedfile will save the content of the uploaded file in the memory, wasting resources.
The uploadedfiledefafilefileimpl class uses a transitional field to save the pointer of the fileitem instance. It is used only when the getinputstream () method is called to obtain the content of the uploaded file. This implementation saves memory space. However, if it is serialized, you will not be able to get the file content after the deserialization is restored. Therefore, the backend bean of the file upload form cannot be stored in the session scope, because when the application is restarted or the server is disabled, the application server serializes the session) bean.
If you want to get a working and efficient solution, save the background bean in the request scope and in <X: specify storage = "file" in inputfileupload> to save memory resources. You can also solve the serialization problem of the uploadedfiledefafilefileimpl class by adding the writeobject () method (which is used to serialize the content of the uploaded file. To ensure the efficiency of uploadedfile, the corresponding readobject () method should create a temporary file instead of reading content from the memory.
When using MyFaces, you also need to pay attention to one problem: the uploadedfile interface does not define a method for deleting temporary files uploaded to the disk by General files. When the fileitem instance is cleared as garbage, these files should be deleted. The defaultfileitem class uploaded to a common file has a finalize () method. When the objects that manage temporary files are deleted from the memory, it can delete the temporary files they manage. If your applications are uploading large files, you may want to delete them immediately after they are processed, instead of waiting for the garbage removal process. To implement this function, you must add a getfileitem () method (in uploadedfiledefaultfileimpl). It should return the fileitem instance, and the instance has the delete () method.
Sample Application
The previous section describes how MyFaces supports file upload with the help of generic file upload. Now let's look at an application that actually utilizes this feature. JSF form (myform. JSP) allows users to select a file and a message digest algorithm, backend Bean (mybean. java) use this algorithm to calculate a hash value and display it on another web page (myresult. JSP. These pages are combined with background beans through a JSF configuration file (faces-config.xml.
Myform. jsp page
This JSF form uses the <X: inputfileupload> flag of MyFaces, some other standard JSF tags are also used to present tags, messages, drop-down lists containing message digest algorithms, and JSF expressions (# {mybean. processmyfile}) specifies the command button for processing uploaded files:
<% @ Taglib uri = "http://java.sun.com/jsf/core" prefix = "F" %> <% @ Taglib uri = "http://java.sun.com/jsf/html" prefix = "H" %> <% @ Taglib uri = "http://myfaces.apache.org/extensions" prefix = "X" %><F: View> <H: Form ID = "myform" enctype = "multipart/form-Data"> <H: messages globalonly = "true" styleclass = "message"/> <H: panelgrid columns = "3" border = "0" cellspacing = "5"> <H: outputlabel for = "myfileid" value = "file:"/> <X: inputfileupload id = "myfileid" value = "# {mybean. myfile}" Storage = "file" required = "true"/> <H: Message for = "myfileid"/> <H: outputlabel for = "myparamid" value = "Param:"/> <H: selectonemenu id = "myparamid" value = "# {mybean. myparam}" required = "true"> <F: selectitem itemlabel = "" itemvalue = ""/> <F: selectitem itemlabel = "MD5" itemvalue = "MD5"/> <F: selectitem itemlabel = "SHA-1" itemvalue = "SHA-1"/> <F: selectitem itemlabel = "SHA-256" itemvalue = "SHA-256"/> <F: selectitem itemlabel = "SHA-384" itemvalue = "SHA-384"/> <F: selectitem itemlabel = "SHA-512" itemvalue = "SHA-512"/> </H: selectonemenu> <H: Message for = "myparamid"/> <H: outputtext value = ""/> <H: commandbutton value = "Submit" Action = "# {mybean. processmyfile}"/> <H: outputtext value = ""/> </H: panelgrid> </H: Form> </F: View> |
Mybean class
Backend beans have three attributes: myfile, myparam, and myresult. The role of the myfile attribute has been explained earlier. It allows you to obtain the content, file name, file size, and content type of the uploaded file. The value of the myparam attribute is the packet Digest algorithm. The myresult property will save the hash value after the processmyfile () method is executed. The mybean class provides get and set methods for all its attributes:
Package com. devsphere. Articles. jsfupload; Import org. Apache. MyFaces. Custom. fileupload. uploadedfile; ... Public class mybean { Private uploadedfile myfile; Private string myparam; Private string myresult; Public uploadedfile getmyfile (){ Return myfile; } Public void setmyfile (uploadedfile myfile ){ This. myfile = myfile; } Public String getmyparam (){ Return myparam; } Public void setmyparam (string myparam ){ This. myparam = myparam; } Public String getmyresult (){ Return myresult; } Public void setmyresult (string myresult ){ This. myresult = myresult; } ... } |
Processmyfile () obtains the content of the uploaded file through an input stream, which is obtained through myfile. getinputstream. The hash value is calculated with the help of Java. Security. messagedigest, and then converted to a string so that it can be accessed by the myresult attribute:
Package com. devsphere. Articles. jsfupload; ... Import javax. Faces. application. facesmessage; Import javax. Faces. Context. facescontext;Import java. Security. messagedigest; Import java. Security. nosuchalgorithmexception; Import java. Io .*; Public class mybean { ... Public String processmyfile (){ Try { Messagedigest MD = messagedigest. getinstance (myparam ); Inputstream in = new bufferedinputstream ( Myfile. getinputstream ()); Try { Byte [] buffer = new byte [64*1024]; Int count; While (COUNT = in. Read (buffer)> 0) Md. Update (buffer, 0, count ); } Finally { In. Close (); } Byte hash [] = md. Digest (); Stringbuffer Buf = new stringbuffer (); For (INT I = 0; I Int B = hash [I] & 0xff; Int c = (B> 4) & 0xf; C = C <10? '0' + C: 'A' + C-10; Buf. append (char) C ); C = B & 0xf; C = C <10? '0' + C: 'A' + C-10; Buf. append (char) C ); } Myresult = Buf. tostring (); Return "OK "; } Catch (exception X ){ Facesmessage message = new facesmessage ( Facesmessage. severity_fatal, X. getclass (). getname (), X. getmessage ()); Facescontext. getcurrentinstance (). addmessage (null, message ); Return NULL; } } } |
Faces-config.xml files
The JSF configuration file defines the backend bean in the request scope and specifies a navigation rule:
<? XML version = "1.0" encoding = "UTF-8"?> <! Doctype faces-config public "-// Sun Microsystems, Inc. // DTD JavaServer faces config 1.1 // en" Http://java.sun.com/dtd/web-facesconfig_1_1.dtd> <Faces-config> <Managed-bean> <Managed-bean-Name> mybean </managed-bean-Name> <Managed-bean-class> Com. devsphere. Articles. jsfupload. mybean </Managed-bean-class> <Managed-bean-scope> request </managed-bean-scope> </Managed-bean> <Navigation-Rule> <From-View-ID>/myform. jsp </from-View-ID> <Navigation-case> <From-outcome> OK </from-outcome> <To-View-ID>/myresult. jsp </to-View-ID> </Navigation-case> </Navigation-Rule> </Faces-config> |
Myresult. jsp page
This web page displays some information about the uploaded file:
<% @ Taglib uri = "http://java.sun.com/jsf/core" prefix = "F" %> <% @ Taglib uri = "http://java.sun.com/jsf/html" prefix = "H" %><F: View> <H: panelgrid columns = "2" border = "0" cellspacing = "5"> <H: outputtext value = "filename:"/> <H: outputtext value = "# {mybean. myfile. name}"/> <H: outputtext value = "filesize:"/> <H: outputtext value = "# {mybean. myfile. Size}"/> <H: outputtext value = "Param:"/> <H: outputtext value = "# {mybean. myparam}"/> <H: outputtext value = "Result:"/> <H: outputtext value = "# {mybean. myresult}"/> </H: panelgrid> </F: View> |
The displayed file name may contain the complete path of the client file system, as shown below:
Figure 2: output information generated on the result page |
Summary
In many cases, users need to upload files through a browser, but there is no ideal way to process these files on the server. Saving the file content in the memory is acceptable for small files, but storing the uploaded file in a temporary file makes this situation more complicated. MyFaces allows you to select a solution suitable for application requirements, but this framework component also has some minor problems. When you no longer need temporary files, it cannot let you delete them; the file name sometimes has a file path; when the user uploads a large file, there is no warning. These defects can be repaired, because we can use the source code, and this article introduces some improvements to the MyFaces code. In any case, for most applications, MyFaces does not need to be modified. The examples in this article are tested using the JSF 1.1.01, MyFaces 1.0.9, and universal File Upload 1.0 environment.