精灵巫婆 发表于 2015-1-18 11:10:02

JAVA编程:利用JSF和MyFaces完成文件上载

多谢指点,其实我对.net和ruby也不是很了解,对与java也只是刚起步的阶段,只是在学习中有了点想法就写出来了,现在俺本科还没毕业,所以对大型项目基本上也没有什么经验。js  Web扫瞄器为我们使用Web使用程序发送文件供应了一条复杂的路子,可是以后版本的JavaWeb尺度(servlets、JSP和JSF)却没法为我们供应任何匡助。侥幸的是,有一些第三方框架组件(比方ApacheCommonsFileUpload、ApacheMyFaces和OracleADFFaces)完成了这类特征,它们都表露了复杂的API和定制的标志(tag)。

  本文的后面一部分先容文件上载操纵是怎样完成的,注释了MyFaces和通用文件上载(前者在外部利用了后者)。我们懂得这些开放源代码框架组件的外部事情体例长短常主要的,由于只要如许我们才干高效力地利用它们,才干依据本人的必要对它们举行修正。本文的第二部分将先容一个复杂的使用程序,它让用户利用web扫瞄器上载文件。

  基于Web的文件上载

  我们把"上载"这个专业术语用得有点过滥了。Web办理员在本人的站点上公布文件的时分,它会说本人"上载"了一个文件。Web开辟者在创建HTML窗体和剧本,让一般用户经由过程web扫瞄器发送一个文件的时分,他也会说本人完成了文件"上载"功效。

  这两种寄义在某些方面是堆叠的,由于web办理员大概利用基于web的界面来公布文件(页面、图象、剧本等等)。收费供应团体web站点的公司(比方Yahoo),完成基于web的文件上载功效的目标是为了让人们上载本人的页面。它同意任何一个具有web扫瞄器和互联网会见才能的团体公布一个小型的web站点。可是,今朝有一些更好的、可用于公布web内容的体例,比方FTP或平安FTP。在这类情形下,你能够使用公用的使用程序(比方FTP客户端)取代web扫瞄器把本人的内容上载到web服务器上。

  本文从web开辟者的角度来会商文件上载的成绩。比方,一个基于web的邮件使用程序(比方Yahoo邮件),就完成了文件上载,由于只要如许,用户才干够发送那些带有附件的动静。另外一个对照好的例子是找事情的web站点,它同意用户把本人的简历发送给雇用职员。本文的示例使用程序盘算上载文件的散列值。你能够在本人的使用程序中对上载的文件举行恣意的处置,比方把它们的内容存储在数据库中或把它们作为邮件附件发送。如今,让我们看看怎样在web使用程序中完成文件上载吧。

  一个HTML窗体中能够包括一个或多个<inputtype="file">元素,扫瞄器把它们作为输出字段来显现(出现),在这些字段中同意用户输出文件路径。在每一个文件输出字段的前面,web扫瞄器会增加一个按钮,它会翻开一个文件对话框,让用户选择一个文件(取代输出路径):


:包括文件输出字段的web窗体
  当用户点击窗体的"提交(Submit)"按钮的时分,web扫瞄器对窗体的数据举行编码,个中包含被选中的文件、文件名(或路径)和窗体的别的一些参数。接着,扫瞄器把编码后的数据发送给web服务器,web服务器把这些数据传送给<form>标志的action属性所指定的剧本来处置。假如你开辟一个Javaweb使用程序,那末操纵剧本多是一个servlet(小服务程序)或JSP页面。

  因为窗体数据默许的编码体例和默许的GET办法都不合适文件上载,以是包括文件输出字段的窗体必需在<form>标志中指定multipart/form-data编码体例和POST办法:

<formenctype="multipart/form-data"method="POST"action="...">
...
<inputtype="file"name="...">
...
</form>

  可是事变并不是这么复杂,由于完成servlet和JSP标准的使用程序服务器纷歧定可以处置multipart/form-data编码体例。因而,你必需剖析哀求的输出流,比方Apache通用文件上载就是一个小型的Java程序包,它让你可以从编码的数据中猎取被上载文件的内容。这个程序包的API是很天真的,它同意你把小文件存储在内存中,把年夜文件存储在磁盘的一时目次中。你能够指定一个文件巨细的阀值,年夜于这个值的文件城市被写到磁盘上,而不会保存在内存中,并且你还能够划定同意的被上载文件的最年夜巨细。

  后面提到的org.apache.commons.fileupload程序包包括一个叫作DiskFileUpload的类,它的parseRequest()办法猎取HttpServletRequest参数,并前往org.apache.commons.fileupload.FileItem实例列表。编码后的窗体数据从servlet哀求的getInputStream()办法所前往的数据流流中读取。FileItem这个名字简单令人曲解,由于这个接口的实例同时体现了被上载的文件和一般的哀求参数。

  通用文件上载程序包所供应的API付与了你会见分化后的窗体数据的权力,可是servlet哀求的getParameter()和getParameterValues()办法却没法事情。这是一个困难,由于在输出字段、反省框、单选框和列表框背景运转的JSF组件必要挪用这两个办法。我们能够使用ServletsAPI供应的两个特征(过滤器和哀求包装器)来办理这个成绩。下一部分形貌了ApacheMyFaces怎样完成过滤器,增加了大批需要的对文件上载的撑持,而不会中止已有的JSF组件。别的,MyFaces为JavaBeans供应了API,同时还供应了一个定制的JSF组件,它体现为<inputtype="file">元素。
<P>  设置JSF和MyFaces扩大

  今朝,JSF标准的次要完成是JSF参考完成(RI),与此同时,Apache供应了别的一个完成,就是MyFaces。固然大概存在别的一些JSF完成,可是JSFRI和MyFaces是个中最盛行的两个。良多开辟者更喜好前者,由于Sun把它称为"官方的"完成,可是,MyFaces具有一些风趣的扩大(比方对上载文件操纵的撑持)。假如你乐意,能够把MyFaces扩大与Sun的JSFRI一同利用。你只必要把myfaces-extensions.jar文件、JSFRI的JAR文件和commons-fileupload-1.0.jar文件一同放到本人的web使用程序的WEB-INF/lib目次中就能够了。上面是必要的JAR文件:

JSF1.1RIjsf-api.jarjsf-impl.jar
JSTL1.1RIjstl.jarstandard.jar
MyFaces扩大myfaces-extensions.jar
ApacheCommons(供JSF和MyFaces扩大利用)commons-collections.jarcommons-digester.jarcommons-beanutils.jarcommons-logging.jarcommons-fileupload-1.0.jar

  org.apache.myfaces.component.html.util程序包中的MultipartRequestWrapper类充任MyFaces和通用文件上载之间的桥梁。这个类扩大了HttpServletRequestWrapper,重载了getParameterMap()、getParameterNames()、getParameter()和getParameterValues()办法,如许它们才干顺应multipart/form-data编码体例。别的,MultipartRequestWrapper供应了两个新的办法,分离是getFileItem()和getFileItems(),它同意你经由过程org.apache.commons.fileupload.FileItem接口来会见被上载的文件。

  当MyFaces检测到编码体例的时分,org.apache.myfaces.component.html.util程序包中的ExtensionsFilter就创建MultipartRequestWrapper实例。因而,你不必要体贴窗体数据的分化,可是假如你但愿改动被上载文件的处置体例,那末懂得它是怎样完成的就很有效处了。在典范的使用程序中,你必需在本人的web使用程序的web.xml形貌信息中设置ExtensionsFilter,如许它才干在JSF的FacesServlet后面截取HTTP哀求:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEweb-appPUBLIC
"-//SunMicrosystems,Inc.//DTDWebApplication2.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>10m</param-value>
</init-param>
<init-param>
<param-name>uploadThresholdSize</param-name>
<param-value>100k</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>

  后面示例中的两个过滤器(filter)参数告知MyFaces把小于100K的文件放进内存中,而且疏忽(即不处置)占用10MB以上磁盘空间的文件。巨细介于uploadThresholdSize和uploadMaxFileSize之间的文件将作为一时文件存储在磁盘上。假如你试图载进一个太年夜的文件,MyFaces以后版本将疏忽一切的窗体数据,就像用户提交了一个空表单一样。假如你但愿给上载文件失利的用户一些提醒信息,就必要变动MyFaces的MultipartRequestWrapper类,找到捕获SizeLimitExceededException非常的地位,并利用FacesContext.getCurrentInstance().addMessage()来告诫用户。

  后面提到,MyFaces扩大包括了文件上载组件,我们能够在JSF页面中利用它。下一部分将先容怎样完成如许的操纵。利用MyFaces的文件上载组件。

  为了在web页面中利用JSF和MyFaces,你必需在<%@taglib%>指令中声明它们的标志库:

<%@tagliburi="http://java.sun.com/jsf/core"prefix="f"%>
<%@tagliburi="http://java.sun.com/jsf/html"prefix="h"%>
<%@tagliburi="http://myfaces.apache.org/extensions"prefix="x"%>

  JSF的<h:form>标志没有method属性,这是由于它只撑持POST办法,但它具有enctype属性,假如你但愿上载文件,就必需利用该属性,把窗体数据设置为多部分(multipart)编码范例:

<f:view>
<h:formid="MyForm"enctype="multipart/form-data">
...
<x:inputFileUploadid="myFileId"
value="#{myBean.myFile}"
storage="file"
required="true"/>
...
</h:form>
</f:view>

  MyFaces的<x:inputFileUpload>标志使你可以界说UI组件的属性,它的出现部件(renderer)天生<inputtype="file">元素。org.apache.myfaces.custom.fileupload程序包包括了UI组件类(HtmlInputFileUpload)、出现部件(HtmlFileUploadRenderer)、定制的标志处置程序(HtmlInputFileUploadTag)、UploadedFile接口和别的一些帮助类。HtmlInputFileUpload类扩大了JSF尺度的HtmlInputText组件,重载了它的一些办法。HtmlFileUploadRenderer卖力天生HTML标志,从MultipartRequestWrapper中猎取FileItem。

  MyFaces并没有让你间接会见通用文件上载所创建的FileItem实例,它供应了本人的UploadedFile接口来猎取被上载的文件的内容、内容范例、文件名和文件巨细。JSF窗体的背景bean必需具有一个UploadedFile范例的属性。后面的例子用JSF表达式(#{myBean.myFile})把UI组件的值绑定到了如许一个bean上。JSF框架组件将猎取HtmlInputFileUpload组件的值,它是一个UploadedFile实例,而且会设置背景bean的myFile属性:

importorg.apache.myfaces.custom.fileupload.UploadedFile;
...
publicclassMyBean{
privateUploadedFilemyFile;

publicUploadedFilegetMyFile(){
returnmyFile;
}

publicvoidsetMyFile(UploadedFilemyFile){
this.myFile=myFile;
}
...
}

  MyFaces具有UploadedFile接口的两种完成体例:UploadedFileDefaultMemoryImpl和UploadedFileDefaultFileImpl。当<x:inputFileUpload>标志没有带storage属性,大概这个属性的值是memory的时分,就利用前者。当storage属性的值是file的时分利用后者。

  UploadedFileDefaultMemoryImpl类从FileItem实例中猎取被上载文件的内容、文件名、文件巨细和内容范例,并把一切这些信息存储在公有字段中。因而,即便通用文件上载把这个文件保留在磁盘上,UploadedFile的这类完成也会把被上载文件的内容保留在内存中,华侈了资本。

  UploadedFileDefaultFileImpl类利用一个过渡字段来保留FileItem实例的指针,仅仅在挪用getInputStream()办法的时分,才利用它来猎取被上载文件的内容。这类完成勤俭了内存空间,可是,假如它是序列化的(serialize),那末在复原序列化(deserialization)以后,你就没法猎取文件内容了。因而,文件上载窗体的背景bean不克不及保留在session感化域(scope)中,由于当使用程序从头启动或服务器封闭的时分,使用程序服务器会序列化对话(session)bean。

  假如你但愿失掉了一个事情一般的、效力很高的办理计划,那末就把背景bean保留在request感化域中,并在<x:inputFileUpload>中指定storage="file"以勤俭内存资本。你也能够经由过程增加writeObject()办法(它用于序列化被上载文件的内容)来办理UploadedFileDefaultFileImpl类的序列化成绩。为了包管UploadedFile的这类完成的高效性,响应的readObject()办法应当从头创建一个一时文件,而不是从内存中读取内容。

  利用MyFaces的时分还必要注重一个成绩:UploadedFile接口并没有界说一个用于删除通用文件上载在磁盘上所创建的一时文件的办法。当FileItem实例作为渣滓被清算的时分,就应当删失落这些文件。通用文件上载的DefaultFileItem类具有一个finalize()办法,当办理一时文件的对象被从内存中删除的时分,它能够删除它们所办理的一时文件。假如你的使用程序正在上载年夜型文件的时分,你大概但愿在它们被处置后当即删除,而不必守候渣滓扫除历程。为了完成如许的功效,你必需增加一个getFileItem()办法(在UploadedFileDefaultFileImpl中),它应当前往FileItem实例,而该实例具有delete()办法。
<P>  示例使用程序

  后面的部分先容了是MyFaces怎样在通用文件上载的匡助下撑持文件上载的。如今我们来看一个实践的使用了这类特征的使用程序。JSF窗体(MyForm.jsp)让用户选择一个文件和一个报文择要算法,背景bean(MyBean.java)利用这个算法盘算出一个散列值,显现在别的一个web页面(MyResult.jsp)中。这些页面和背景bean经由过程一个JSF设置文件(faces-config.xml)分离在一同。

  MyForm.jsp页面

  这个JSF窗体利用了MyFaces的<x:inputFileUpload>标志,同时还利用了别的一些尺度的JSF标志来出现标签、动静、包括报文择要算法的下拉列表、利用JSF表达式(#{myBean.processMyFile})指定了处置被上载文件的操纵办法的命令按钮:

<%@tagliburi="http://java.sun.com/jsf/core"prefix="f"%>
<%@tagliburi="http://java.sun.com/jsf/html"prefix="h"%>
<%@tagliburi="http://myfaces.apache.org/extensions"prefix="x"%>

<f:view>
<h:formid="MyForm"enctype="multipart/form-data">
 <h:messagesglobalOnly="true"styleClass="message"/>
 <h:panelGridcolumns="3"border="0"cellspacing="5">
 <h:outputLabelfor="myFileId"value="File:"/>
 <x:inputFileUploadid="myFileId"value="#{myBean.myFile}"storage="file"required="true"/>
 <h:messagefor="myFileId"/>
 <h:outputLabelfor="myParamId"value="Param:"/>
 <h:selectOneMenuid="myParamId"value="#{myBean.myParam}"required="true">
 <f:selectItemitemLabel=""itemValue=""/>
 <f:selectItemitemLabel="MD5"itemValue="MD5"/>
 <f:selectItemitemLabel="SHA-1"itemValue="SHA-1"/>
 <f:selectItemitemLabel="SHA-256"itemValue="SHA-256"/>
 <f:selectItemitemLabel="SHA-384"itemValue="SHA-384"/>
 <f:selectItemitemLabel="SHA-512"itemValue="SHA-512"/>
</h:selectOneMenu>
<h:messagefor="myParamId"/>

<h:outputTextvalue=""/>
<h:commandButtonvalue="Submit"
action="#{myBean.processMyFile}"/>
<h:outputTextvalue=""/>

</h:panelGrid>
</h:form>
</f:view>

  MyBean类

  背景bean具有三个属性:myFile、myParam和myResult。后面已注释了myFile属性的脚色。它让你猎取被上载文件的内容和文件名、文件巨细、内容范例。myParam属性的值是报文择要算法。myResult属性将保留processMyFile()办法实行以后的散列值。MyBean类为本人的一切属性供应了get和set办法:

packagecom.devsphere.articles.jsfupload;

importorg.apache.myfaces.custom.fileupload.UploadedFile;
...
publicclassMyBean{
 privateUploadedFilemyFile;
 privateStringmyParam;
 privateStringmyResult;

 publicUploadedFilegetMyFile(){
  returnmyFile;
 }

 publicvoidsetMyFile(UploadedFilemyFile){
  this.myFile=myFile;
 }

 publicStringgetMyParam(){
  returnmyParam;
 }

 publicvoidsetMyParam(StringmyParam){
  this.myParam=myParam;
 }

 publicStringgetMyResult(){
  returnmyResult;
 }

 publicvoidsetMyResult(StringmyResult){
  this.myResult=myResult;
 }
 ...
}

  processMyFile()经由过程一个输出流猎取被上载文件的内容,这个输出流是经由过程myFile.getInputStream()取得的。散列值是在java.security.MessageDigest的匡助下盘算出来的,然后把它转换为字符串,如许就能够被myResult属性会见了:

packagecom.devsphere.articles.jsfupload;
...
importjavax.faces.application.FacesMessage;
importjavax.faces.context.FacesContext;

importjava.security.MessageDigest;
importjava.security.NoSuchAlgorithmException;

importjava.io.*;

publicclassMyBean{
 ...
 publicStringprocessMyFile(){
  try{
   MessageDigestmd=MessageDigest.getInstance(myParam);
   InputStreamin=newBufferedInputStream(
   myFile.getInputStream());
   try{
    byte[]buffer=newbyte;
    intcount;
    while((count=in.read(buffer))>0)
     md.update(buffer,0,count);
  }finally{
   in.close();
  }
  bytehash[]=md.digest();
  StringBufferbuf=newStringBuffer();
  for(inti=0;i<hash.length;i++){
   intb=hash&0xFF;
   intc=(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(Exceptionx){
  FacesMessagemessage=newFacesMessage(
  FacesMessage.SEVERITY_FATAL,
  x.getClass().getName(),x.getMessage());
  FacesContext.getCurrentInstance().addMessage(null,message);
  returnnull;
 }
}

}

  faces-config.xml文件

  JSF设置文件把背景bean界说在request感化域中,而且指定了一个导航划定规矩:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEfaces-configPUBLIC
"-//SunMicrosystems,Inc.//DTDJavaServerFacesConfig1.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页面

  这个web页面显现了被上载文件的一些信息:

<%@tagliburi="http://java.sun.com/jsf/core"prefix="f"%>
<%@tagliburi="http://java.sun.com/jsf/html"prefix="h"%>

<f:view>

<h:panelGridcolumns="2"border="0"cellspacing="5">

<h:outputTextvalue="FileName:"/>
<h:outputTextvalue="#{myBean.myFile.name}"/>

<h:outputTextvalue="FileSize:"/>
<h:outputTextvalue="#{myBean.myFile.size}"/>

<h:outputTextvalue="Param:"/>
<h:outputTextvalue="#{myBean.myParam}"/>

<h:outputTextvalue="Result:"/>
<h:outputTextvalue="#{myBean.myResult}"/>

</h:panelGrid>

</f:view>

  被显现的文件名大概带有客户端文件体系的完全路径,以下所示:


:了局页面发生的输入信息
  总结

  在良多情形下,用户必要经由过程扫瞄器上载文件,可是在服务器端却没有处置这些文件的幻想办法。把文件的内容保留在内存中关于小型文件来讲是能够承受的,可是把被上载的文件存储在一时文件中使这类情形变庞大了。MyFaces让你可以选择一个合适使用程序需求的办理计划,可是这个框架组件也有一些小成绩。当你不再必要一时文件的时分,它没法让你删除它们;文件名偶然候带有文件路径;当用户上载过年夜的文件的时分也没有告诫信息。这些缺点是能够修补的,由于我们可使用源代码,并且本文就先容了MyFaces代码能够改善的一些的中央。不管怎样,关于多半使用程序来讲,MyFaces不做修正就符合需求。本文的例子是利用JSF1.1.01、MyFaces1.0.9和通用文件上载1.0情况测试的。
专门做了这个例子;而java的这个例子好像就是为了教学而写的,很多教学目的的例子是不考虑优化、性能的。

蒙在股里 发表于 2015-1-20 15:44:42

自从Sun推出Java以来,就力图使之无所不包,所以Java发展到现在,按应用来分主要分为三大块:J2SE,J2ME和J2EE,这也就是Sun ONE(Open Net Environment)体系。J2SE就是Java2的标准版,主要用于桌面应用软件的编程;J2ME主要应用于嵌入是系统开发,如手机和PDA的编程;J2EE是Java2的企业版,主要用于分布式的网络程序的开发,如电子商务网站和ERP系统。

灵魂腐蚀 发表于 2015-1-23 10:36:37

如果要向java web方向发展也要吧看看《Java web从入门到精通》学完再到《Struts2.0入门到精通》这样你差不多就把代码给学完了。有兴趣可以看一些设计模块和框架的包等等。

爱飞 发表于 2015-1-31 15:39:07

Java自面世后就非常流行,发展迅速,对C++语言形成了有力冲击。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于个人PC、数据中心、游戏控制台

冷月葬花魂 发表于 2015-2-2 22:05:54

Java是一种计算机编程语言,拥有跨平台、面向对java

海妖 发表于 2015-2-8 09:05:01

有时间再研究一下MVC结构(把Model-View-Control分离开的设计思想)

老尸 发表于 2015-2-25 07:22:15

J2SE开发桌面应用软件比起 VC,VB,DEPHI这些传统开发语言来说,优势好象并不明显。J2ME对于初学者来说,好象又有点深奥,而且一般开发者很难有开发环境。

透明 发表于 2015-3-7 17:47:08

还好,SUN提供了Javabean可以把你的JSP中的 Java代码封装起来,便于调用也便于重用。

简单生活 发表于 2015-3-15 10:31:38

当然你也可以参加一些开源项目,一方面可以提高自己,另一方面也是为中国软件事业做贡献嘛!开发者在互联网上用CVS合作开发,用QQ,MSN,E-mail讨论联系,天南海北的程序员分散在各地却同时开发同一个软件,是不是很有意思呢?

只想知道 发表于 2015-3-22 00:10:27

我大二,Java也只学了一年,觉得还是看thinking in java好,有能力的话看英文原版(中文版翻的不怎么好),还能提高英文文档阅读能力。
页: [1]
查看完整版本: JAVA编程:利用JSF和MyFaces完成文件上载