1. 引言
每位Web開發者在工程中都必須實現至少實現一個客戶文件的上載功能。永遠需要!然而,要求用戶僅提供一個指向其數據的
URL是不公平的。作為一個開發者,幫助用戶順利地完成這些正是你的工作。HTTP協議的使用將十分有助于解決這個問題,但是許多開發者并沒有選擇使用HTTP協議。
你需要解決的問題有:存儲上載的文件并找到關于問題"Where?","Why?"和"How?"等等的有關答案。
本文將解釋所有在解決這些問題中遇到的瓶頸,并提供了功能性的、易于理解的代碼,這些內容很可能會應用于你將來的工程中。
2. 準備工作 本文將使用當前最流行的開發工具,它們是:
·應用程序服務器:WebLogic 8.1 SP3服務器
·基于Java的構建工具:Apache
Ant 1.6.2
·數據庫服務器:MySQL 4.0.16
·用于從Java連接到MySQL的:MySQL Connector/J 3.1.7
Struts 1.2.4用作構建Java Web應用程序的框架,而Hibernate 3.0(RC1)用于對象/關系持續性操作和查詢服務。
本文雖然基于Windows平臺寫成,但在其它操作系統之上,應該稍作修改就能運行。
另外,讀者還應熟悉BEA WebLogic服務器以及使用Struts和Hibernate進行J2EE應用程序的開發。篇幅所限,本文并沒有討論關于應用程序和數據庫服務器配置的問題。
3. 上載工程分析 現在讓我們討論存儲上載文件的機制,并回答上面列出的三個問題。
·Where?你將會把上載文件存儲到一個數據庫中。
·Why?在許多情況下,它確實是合適的解決方案。使用本文的解決方案,你不會因同步上載文件而煩惱,一旦你正在備份著應用程序-你只需要備份數據庫就可以了。而且,你不需要與一個用戶及其在一文件系統上的文件一直保持十分笨拙的聯系。
·How?可以使用BLOB(二進制大型對象)字段實現。這樣的字段用于存儲大型的并且經常是原始或二進制的格式。Hibernate可以使你非常容易地操作這些字段。
典型情況下,一個企業應用程序(EAR)由兩部分組成:Web層(WAR)和商業層(EJB)。商業層包含一個無狀態的會話
bean-它借助于Hibernate的幫助實現數據的存儲。圖1顯示了EJB的遠程接口。
 圖1.HelloSession EJB的接口。 |
從Web層角度看,這個EJB為商業代理所存取。
注意,該代碼使用了一個類User的對象。User代表
什么意思?它是一個保留在數據庫中的用戶實體的"Plain Old Java Object"(POJO)。你將會活躍地使用這個UserPOJO。設置它的屬性并請求EJB來存儲它,然后帶回一個所有的已存在于數據庫中的User實體的列表。或者,由它取回一個專門的User實體并存入POJO中,然后使用一個getter存取器來存取它的屬性(見圖2)。
 圖2.所有的Web層的servlet都使用該User POJO。 |
非常明顯,Web層僅由三個servlet(Struts Action的)組成,一個用于上載文件,一個用于下載文件,一個用于列出所有的User實體及其相關文件。
·DownloadFileAction:該servlet僅使用一個參數id,這是在數據庫中的一個用戶的id。然后,它裝入該用戶的實體并把該文件從BLOB字段傳遞到ServletOutputStream。
·UploadFileAction:該servlet負責從一個HTML表單中提取數據并用這些數據進一步生成DynaActionForm。它僅提取用戶名和上載的文件。
·ListAllFilesAction:該servlet沒有輸入參數或數據,僅負責從數據庫中裝入所有可用的用戶User實體。
4. 環境準備
如果所有相應的軟件被正確下載并安裝在你的PC上,那么下一步,你就可以準備數據庫和存取該數據庫的用戶而且還要使用名為MySqlDS的JNDI設置好連接池與數據源。同時,請肯定MySQL Connector/J存在于你的類路徑中!要檢查這一點,只需輸入:
echo %CLASSPATH%
文件mysql-connector-java-3.1.7-bin.jar(帶有完整的路徑)應該于此。這是必需的,因為WebLogic需要查找到MySQL Connector/J并用該驅動程序進行工作。
現在,既然一切準備妥當,我們就可以在數據庫中創建一個表以用于保存用戶實體。以任何MySQL客戶身份登錄到該數據庫,然后輸入:
drop table if exists TABLE_USER_FILE; create table TABLE_USER_FILE ( USER_ID int auto_increment not null, USER_USERNAME text, USER_FILENAME text, USER_FILETYPE text, USER_FILESIZE text, USER_FILEBIN longblob, primary key (USER_ID) ) type = MyISAM; |
你看,一切都很簡單。也許在此你最陌生的是longblob類型。二進制大型對象(BLOB)列是MySQL的秘密武器之一。這些列中存儲了二進制的數據,你可以象其它普通的數據類型一樣來檢索和操縱它。根據MySQL指南有關資料,BLOB是一個二進制大型對象,它能容納不同大小的數據。事實上,MySQL有四種BLOB類型:
·tinyblob:僅255個字符
·blob:最大限制到65K字節
·mediumblob:限制到16M字節
·longblob:可達4GB
在每個MySQL的文檔(從MySQL4.0開始)的介紹中,一個longblob列的最大允許長度依賴于在客戶/服務器協議中可配置的最大包的大小和可用內存數。
你可能對在BLOB中存儲大型文件非常謹慎,但是請放心使用,MYSQL提供了這樣的靈活性!最大包的大小可容易地通過文件my.ini中的適當行進行設置。例如:
set-variable = max_allowed_packet=10M
你能指定幾乎任何你需要的大小。默認是1M。
現在,在Hibernate和WebLogic之間還存在一個問題。根據Hibernate的文檔,Hibernate3.0使用ANTLR作為它的新的查詢分析器。這真是個遺憾!但是BEA Weblogic在系統類路徑中包括了ANTLR的一個版本,它在任何應用程序庫裝入前就已經被加載了。因為Weblog似乎不支持屬性類裝載器隔離,在應用程序的上下文中它是不會看到該Hibernate類的。BEA好象在包名前加上前綴來解決這個問題,但是現已發布的ANTLR并沒有這個前綴。
這個問題的解決辦法是,把所有的Hibernate和依賴庫放到你的CLASSPATH中。就象如下這樣:
C:\green\te3>echo %CLASSPATH%
.;C:\mysql-connector-java-3.1.7\mysql-connector-java-3.1.7-bin.jar;C:\hibernate-3.0\hibernate3.jar;
C:\hibernate-3.0\lib\ehcache-1.1.jar;C:\hibernate-3.0\lib\jta.jar;C:\hibernate-3.0\lib\xml-apis.jar;
C:\hibernate-3.0\lib\commons-logging-1.0.4.jar;C:\hibernate-3.0\lib\dom4j-1.5.2.jar;
C:\hibernate-3.0\lib\antlr-2.7.4.jar;C:\hibernate-3.0\lib\cglib-full-2.0.2.jar;
C:\hibernate-3.0\lib\jdbc2_0-stdext.jar;C:\hibernate-3.0\lib\xerces-2.6.2.jar;
C:\hibernate-3.0\lib\jaxen-1.1-beta-4.jar;C:\hibernate-3.0\lib\commons-collections-2.1.1.jar;
C:\hibernate-3.0\lib\log4j-1.2.9.jar;
現在,unzip源代碼到任何你想要的目錄下。用你喜歡的文件編輯器打開build.xml文件,并檢查(如果必要的話,加以改變)前面涉及你的HOME目錄的幾行和你的域的標題。保存你的變化并輸入:
ant
當工程建構完成時,你就會得到一個文件TE3.EAR,這是一個準備好等待發布的包(名字TE3僅是個普通名字)。然后,你就可以用WebLogic的管理控制臺發布它,當發布后,用你的瀏覽器http://localhost:7001/te3/打開它。之后,你將看到兩個選項:"upload file"和"list all files"。
5. 代碼分析
現在,你已經看到了一切是如何工作的,下面解釋一下幾個更為重要的代碼片斷。如前述,UploadFileAction.java使用DynaActionForm來保持HTML表單的屬性。下面是它在/WEB-INF/struts-config.xml文件中的定義:
... <form-beans> <form-bean name="uploadFileForm" type="org.apache.struts.action.DynaActionForm" dynamic="true"> <form-property name="myFile" type="org.apache.struts.upload.FormFile"/> <form-property name="myName" type="java.lang.String"/> </form-bean> </form-beans> ... <action-mappings> <action path="/UploadFile" type="com.prokhorenko.web.UploadFileAction" name="uploadFileForm"> ... |
下面的代碼教你怎樣通過屬性存取來"存儲"一個上傳的文件:
... User per = new User(); DynaActionForm df = (DynaActionForm) form; FormFile myFile = (FormFile) df.get("myFile"); ... per.setFilebin ( Hibernate.createBlob (myFile.getInputStream()) ); ... |
Hibernate.createBlob(...)返回初始的不變的java.sql.Blob對象并使用它,因為為了設置User實體的filebin屬性,該屬性被定義并被映射為java.sql.Blob。
接下去一段有趣的代碼來自于DownloadFileAction.java,它用’id’加載該User實體:
... User per = bd.getUser( new Long((String)request.getParameter("id")) ); ... |
下一步,你就需要設置響應的頭部,并開始把Blob型字段filebin的內容寫到ServletOutputStream中:
... ServletOutputStream outStream = response.getOutputStream(); InputStream in = per.getFilebin().getBinaryStream(); byte[] buffer = new byte[32768]; int n = 0; while ( ( n = in.read(buffer)) != -1) { outStream.write(buffer, 0, n); } in.close(); outStream.flush(); ... |
6. 最容易的解決方案 在所有官方檔案中,Hibernate 3.0都包裝了Blob和Clob的實例,這樣以來就允許具有類型Blob或Clob的屬性的類可以被分離、串行化、反串行化以及被傳遞而實現合并的目的。因此,你會看到,Struts和Hibernate幾乎為你做了一切事情-而需要你做的僅僅是極少的幾個步驟。
現在看來,上載文件并把它們存儲到數據庫中已不再是象以前那樣是一項繁重的任務。你只需選擇正確的工具并知道如何靈活地使用它們即可!
posted on 2006-02-09 13:32
黑咖啡 閱讀(208)
評論(0) 編輯 收藏 所屬分類:
FrameWork