excel
?Connection connection = DbConnectionManager.getConnection();
??????? try{
??????????
??????? ?
??????????? PreparedStatement prst = connection.prepareStatement(sql);
??????????? ResultSet rs = prst.executeQuery();
??????????? if(rs.next()){
??????????? ?//if(rs == null){rs.close();
??????????????? Blob blob = rs.getBlob("bb");
?????????????
??????????????? byte[] ab = blob.getBytes(1, (int)blob.length());
??????????????? //URLEncoder.encode(rname, "utf-8");
??????????????? //new String("文件名.xls".getBytes("GBK"),"ISO8859_1")
??????????????? //response.setHeader("Charset","gb2312");
??????????????? //application/msexcel-comma
??????????????? String fs = new String(rname.getBytes("GBK"),"ISO8859_1");
????????????????
??????????????? response.reset();
??????????????? response.setLocale(java.util.Locale.CHINA);
??????????????? response.setContentType("application/vnd.ms-excel");
??????????????? request.setCharacterEncoding("GBK");
??????????????
??????????????
??????????????? String s = "attachment; filename="+fs;
??????????????? response.setHeader("Content-Disposition", s);
??????????????? ServletOutputStream op = response.getOutputStream();
??????????????? op.write(ab);
??????????????? op.flush();
??????????????? op.close();
??????????? }
???????? }catch(SQLException e){
??????? log.info(e.toString()+"sql:"+sql);
???? }
???????? catch(NullPointerException ex){
??????? ? System.out.print("數(shù)據(jù)為空");
???????? }
????????
???????? finally{
???????? try{
???????? connection.close();
?????? }catch(Exception e){
???? ?? System.out.print("ddd");
?????? }
A. 有兩個方法:
1). int i = Integer.parseInt([String]); 或
i = Integer.parseInt([String],[int radix]);
2). int i = Integer.valueOf(my_str).intValue();
注: 字串轉(zhuǎn)成 Double, Float, Long 的方法大同小異.
2 如何將整數(shù) int 轉(zhuǎn)換成字串 String ?
A. 有叁種方法:
1.) String s = String.valueOf(i);
2.) String s = Integer.toString(i);
3.) String s = "" + i;
注: Double, Float, Long 轉(zhuǎn)成字串的方法大同小異.
JAVA數(shù)據(jù)類型轉(zhuǎn)換 ynniebo [收藏]
關(guān)鍵字 類型轉(zhuǎn)換
出處
這是一個例子,說的是JAVA中數(shù)據(jù)數(shù)型的轉(zhuǎn)換.供大家學(xué)習(xí)引
package cn.com.lwkj.erts.register;
import java.sql.Date;
public class TypeChange {
public TypeChange() {
}
//change the string type to the int type
public static int stringToInt(String intstr)
{
Integer integer;
integer = Integer.valueOf(intstr);
return integer.intValue();
}
//change int type to the string type
public static String intToString(int value)
{
Integer integer = new Integer(value);
return integer.toString();
}
//change the string type to the float type
public static float stringToFloat(String floatstr)
{
Float floatee;
floatee = Float.valueOf(floatstr);
return floatee.floatValue();
}
//change the float type to the string type
public static String floatToString(float value)
{
Float floatee = new Float(value);
return floatee.toString();
}
//change the string type to the sqlDate type
public static java.sql.Date stringToDate(String dateStr)
{
return java.sql.Date.valueOf(dateStr);
}
//change the sqlDate type to the string type
public static String dateToString(java.sql.Date datee)
{
return datee.toString();
}
public static void main(String[] args)
{
java.sql.Date day ;
day = TypeChange.stringToDate("2003-11-3");
String strday = TypeChange.dateToString(day);
System.out.println(strday);
}
}
作者:郎云鵬(dev2dev ID: hippiewolf)
摘要:雖然session機制在web應(yīng)用程序中被采用已經(jīng)很長時間了,但是仍然有很多人不清楚session機制的本質(zhì),以至不能正確的應(yīng)用這一技術(shù)。本文將詳細討論session的工作機制并且對在Java web application中應(yīng)用session機制時常見的問題作出解答。
目錄:
一、術(shù)語session
二、HTTP協(xié)議與狀態(tài)保持
三、理解cookie機制
四、理解session機制
五、理解javax.servlet.http.HttpSession
六、HttpSession常見問題
七、跨應(yīng)用程序的session共享
八、總結(jié)
參考文檔
一、術(shù)語session
在我的經(jīng)驗里,session這個詞被濫用的程度大概僅次于transaction,更加有趣的是transaction與session在某些語境下的含義是相同的。
session,中文經(jīng)常翻譯為會話,其本來的含義是指有始有終的一系列動作/消息,比如打電話時從拿起電話撥號到掛斷電話這中間的一系列過程可以稱之為一個session。有時候我們可以看到這樣的話“在一個瀏覽器會話期間,...”,這里的會話一詞用的就是其本義,是指從一個瀏覽器窗口打開到關(guān)閉這個期間①。最混亂的是“用戶(客戶端)在一次會話期間”這樣一句話,它可能指用戶的一系列動作(一般情況下是同某個具體目的相關(guān)的一系列動作,比如從登錄到選購商品到結(jié)賬登出這樣一個網(wǎng)上購物的過程,有時候也被稱為一個transaction),然而有時候也可能僅僅是指一次連接,也有可能是指含義①,其中的差別只能靠上下文來推斷②。
然而當(dāng)session一詞與網(wǎng)絡(luò)協(xié)議相關(guān)聯(lián)時,它又往往隱含了“面向連接”和/或“保持狀態(tài)”這樣兩個含義,“面向連接”指的是在通信雙方在通信之前要先建立一個通信的渠道,比如打電話,直到對方接了電話通信才能開始,與此相對的是寫信,在你把信發(fā)出去的時候你并不能確認對方的地址是否正確,通信渠道不一定能建立,但對發(fā)信人來說,通信已經(jīng)開始了?!氨3譅顟B(tài)”則是指通信的一方能夠把一系列的消息關(guān)聯(lián)起來,使得消息之間可以互相依賴,比如一個服務(wù)員能夠認出再次光臨的老顧客并且記得上次這個顧客還欠店里一塊錢。這一類的例子有“一個TCP session”或者“一個POP3 session”③。
而到了web服務(wù)器蓬勃發(fā)展的時代,session在web開發(fā)語境下的語義又有了新的擴展,它的含義是指一類用來在客戶端與服務(wù)器之間保持狀態(tài)的解決方案④。有時候session也用來指這種解決方案的存儲結(jié)構(gòu),如“把xxx保存在session里”⑤。由于各種用于web開發(fā)的語言在一定程度上都提供了對這種解決方案的支持,所以在某種特定語言的語境下,session也被用來指代該語言的解決方案,比如經(jīng)常把Java里提供的javax.servlet.http.HttpSession簡稱為session⑥。
鑒于這種混亂已不可改變,本文中session一詞的運用也會根據(jù)上下文有不同的含義,請大家注意分辨。
在本文中,使用中文“瀏覽器會話期間”來表達含義①,使用“session機制”來表達含義④,使用“session”表達含義⑤,使用具體的“HttpSession”來表達含義⑥
二、HTTP協(xié)議與狀態(tài)保持
HTTP協(xié)議本身是無狀態(tài)的,這與HTTP協(xié)議本來的目的是相符的,客戶端只需要簡單的向服務(wù)器請求下載某些文件,無論是客戶端還是服務(wù)器都沒有必要紀錄彼此過去的行為,每一次請求之間都是獨立的,好比一個顧客和一個自動售貨機或者一個普通的(非會員制)大賣場之間的關(guān)系一樣。
然而聰明(或者貪心?)的人們很快發(fā)現(xiàn)如果能夠提供一些按需生成的動態(tài)信息會使web變得更加有用,就像給有線電視加上點播功能一樣。這種需求一方面迫使HTML逐步添加了表單、腳本、DOM等客戶端行為,另一方面在服務(wù)器端則出現(xiàn)了CGI規(guī)范以響應(yīng)客戶端的動態(tài)請求,作為傳輸載體的HTTP協(xié)議也添加了文件上載、cookie這些特性。其中cookie的作用就是為了解決HTTP協(xié)議無狀態(tài)的缺陷所作出的努力。至于后來出現(xiàn)的session機制則是又一種在客戶端與服務(wù)器之間保持狀態(tài)的解決方案。
讓我們用幾個例子來描述一下cookie和session機制之間的區(qū)別與聯(lián)系。筆者曾經(jīng)常去的一家咖啡店有喝5杯咖啡免費贈一杯咖啡的優(yōu)惠,然而一次性消費5杯咖啡的機會微乎其微,這時就需要某種方式來紀錄某位顧客的消費數(shù)量。想象一下其實也無外乎下面的幾種方案:
1、該店的店員很厲害,能記住每位顧客的消費數(shù)量,只要顧客一走進咖啡店,店員就知道該怎么對待了。這種做法就是協(xié)議本身支持狀態(tài)。
2、發(fā)給顧客一張卡片,上面記錄著消費的數(shù)量,一般還有個有效期限。每次消費時,如果顧客出示這張卡片,則此次消費就會與以前或以后的消費相聯(lián)系起來。這種做法就是在客戶端保持狀態(tài)。
3、發(fā)給顧客一張會員卡,除了卡號之外什么信息也不紀錄,每次消費時,如果顧客出示該卡片,則店員在店里的紀錄本上找到這個卡號對應(yīng)的紀錄添加一些消費信息。這種做法就是在服務(wù)器端保持狀態(tài)。
由于HTTP協(xié)議是無狀態(tài)的,而出于種種考慮也不希望使之成為有狀態(tài)的,因此,后面兩種方案就成為現(xiàn)實的選擇。具體來說cookie機制采用的是在客戶端保持狀態(tài)的方案,而session機制采用的是在服務(wù)器端保持狀態(tài)的方案。同時我們也看到,由于采用服務(wù)器端保持狀態(tài)的方案在客戶端也需要保存一個標(biāo)識,所以session機制可能需要借助于cookie機制來達到保存標(biāo)識的目的,但實際上它還有其他選擇。
三、理解cookie機制
cookie機制的基本原理就如上面的例子一樣簡單,但是還有幾個問題需要解決:“會員卡”如何分發(fā);“會員卡”的內(nèi)容;以及客戶如何使用“會員卡”。
正統(tǒng)的cookie分發(fā)是通過擴展HTTP協(xié)議來實現(xiàn)的,服務(wù)器通過在HTTP的響應(yīng)頭中加上一行特殊的指示以提示瀏覽器按照指示生成相應(yīng)的cookie。然而純粹的客戶端腳本如JavaScript或者VBScript也可以生成cookie。
而cookie的使用是由瀏覽器按照一定的原則在后臺自動發(fā)送給服務(wù)器的。瀏覽器檢查所有存儲的cookie,如果某個cookie所聲明的作用范圍大于等于將要請求的資源所在的位置,則把該cookie附在請求資源的HTTP請求頭上發(fā)送給服務(wù)器。意思是麥當(dāng)勞的會員卡只能在麥當(dāng)勞的店里出示,如果某家分店還發(fā)行了自己的會員卡,那么進這家店的時候除了要出示麥當(dāng)勞的會員卡,還要出示這家店的會員卡。
cookie的內(nèi)容主要包括:名字,值,過期時間,路徑和域。
其中域可以指定某一個域比如.google.com,相當(dāng)于總店招牌,比如寶潔公司,也可以指定一個域下的具體某臺機器比如www.google.com或者froogle.google.com,可以用飄柔來做比。
路徑就是跟在域名后面的URL路徑,比如/或者/foo等等,可以用某飄柔專柜做比。
路徑與域合在一起就構(gòu)成了cookie的作用范圍。
如果不設(shè)置過期時間,則表示這個cookie的生命期為瀏覽器會話期間,只要關(guān)閉瀏覽器窗口,cookie就消失了。這種生命期為瀏覽器會話期的cookie被稱為會話cookie。會話cookie一般不存儲在硬盤上而是保存在內(nèi)存里,當(dāng)然這種行為并不是規(guī)范規(guī)定的。如果設(shè)置了過期時間,瀏覽器就會把cookie保存到硬盤上,關(guān)閉后再次打開瀏覽器,這些cookie仍然有效直到超過設(shè)定的過期時間。
存儲在硬盤上的cookie可以在不同的瀏覽器進程間共享,比如兩個IE窗口。而對于保存在內(nèi)存里的cookie,不同的瀏覽器有不同的處理方式。對于IE,在一個打開的窗口上按Ctrl-N(或者從文件菜單)打開的窗口可以與原窗口共享,而使用其他方式新開的IE進程則不能共享已經(jīng)打開的窗口的內(nèi)存cookie;對于Mozilla Firefox0.8,所有的進程和標(biāo)簽頁都可以共享同樣的cookie。一般來說是用javascript的window.open打開的窗口會與原窗口共享內(nèi)存cookie。瀏覽器對于會話cookie的這種只認cookie不認人的處理方式經(jīng)常給采用session機制的web應(yīng)用程序開發(fā)者造成很大的困擾。
下面就是一個goolge設(shè)置cookie的響應(yīng)頭的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html
這是使用HTTPLook這個HTTP Sniffer軟件來俘獲的HTTP通訊紀錄的一部分
瀏覽器在再次訪問goolge的資源時自動向外發(fā)送cookie
使用Firefox可以很容易的觀察現(xiàn)有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。
IE也可以設(shè)置在接受cookie前詢問
這是一個詢問接受cookie的對話框。
四、理解session機制
session機制是一種服務(wù)器端的機制,服務(wù)器使用一種類似于散列表的結(jié)構(gòu)(也可能就是使用散列表)來保存信息。
當(dāng)程序需要為某個客戶端的請求創(chuàng)建一個session的時候,服務(wù)器首先檢查這個客戶端的請求里是否已包含了一個session標(biāo)識 - 稱為session id,如果已包含一個session id則說明以前已經(jīng)為此客戶端創(chuàng)建過session,服務(wù)器就按照session id把這個session檢索出來使用(如果檢索不到,可能會新建一個),如果客戶端請求不包含session id,則為此客戶端創(chuàng)建一個session并且生成一個與此session相關(guān)聯(lián)的session id,session id的值應(yīng)該是一個既不會重復(fù),又不容易被找到規(guī)律以仿造的字符串,這個session id將被在本次響應(yīng)中返回給客戶端保存。
保存這個session id的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規(guī)則把這個標(biāo)識發(fā)揮給服務(wù)器。一般這個cookie的名字都是類似于SEEESIONID,而。比如weblogic對于web應(yīng)用程序生成的cookie,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID。
由于cookie可以被人為的禁止,必須有其他機制以便在cookie被禁止時仍然能夠把session id傳遞回服務(wù)器。經(jīng)常被使用的一種技術(shù)叫做URL重寫,就是把session id直接附加在URL路徑的后面,附加方式也有兩種,一種是作為URL路徑的附加信息,表現(xiàn)形式為http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一種是作為查詢字符串附加在URL后面,表現(xiàn)形式為http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
這兩種方式對于用戶來說是沒有區(qū)別的,只是服務(wù)器在解析的時候處理的方式不同,采用第一種方式也有利于把session id的信息和正常程序參數(shù)區(qū)分開來。
為了在整個交互過程中始終保持狀態(tài),就必須在每個客戶端可能請求的路徑后面都包含這個session id。
另一種技術(shù)叫做表單隱藏字段。就是服務(wù)器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞回服務(wù)器。比如下面的表單
<form name="testform" action="/xxx">
<input type="text">
</form>
在被傳遞給客戶端之前將被改寫成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
這種技術(shù)現(xiàn)在已較少應(yīng)用,筆者接觸過的很古老的iPlanet6(SunONE應(yīng)用服務(wù)器的前身)就使用了這種技術(shù)。
實際上這種技術(shù)可以簡單的用對action應(yīng)用URL重寫來代替。
在談?wù)搒ession機制的時候,常常聽到這樣一種誤解“只要關(guān)閉瀏覽器,session就消失了”。其實可以想象一下會員卡的例子,除非顧客主動對店家提出銷卡,否則店家絕對不會輕易刪除顧客的資料。對session來說也是一樣的,除非程序通知服務(wù)器刪除一個session,否則服務(wù)器會一直保留,程序一般都是在用戶做log off的時候發(fā)個指令去刪除session。然而瀏覽器從來不會主動在關(guān)閉之前通知服務(wù)器它將要關(guān)閉,因此服務(wù)器根本不會有機會知道瀏覽器已經(jīng)關(guān)閉,之所以會有這種錯覺,是大部分session機制都使用會話cookie來保存session id,而關(guān)閉瀏覽器后這個session id就消失了,再次連接服務(wù)器時也就無法找到原來的session。如果服務(wù)器設(shè)置的cookie被保存到硬盤上,或者使用某種手段改寫瀏覽器發(fā)出的HTTP請求頭,把原來的session id發(fā)送給服務(wù)器,則再次打開瀏覽器仍然能夠找到原來的session。
恰恰是由于關(guān)閉瀏覽器不會導(dǎo)致session被刪除,迫使服務(wù)器為seesion設(shè)置了一個失效時間,當(dāng)距離客戶端上一次使用session的時間超過這個失效時間時,服務(wù)器就可以認為客戶端已經(jīng)停止了活動,才會把session刪除以節(jié)省存儲空間。
五、理解javax.servlet.http.HttpSession
HttpSession是Java平臺對session機制的實現(xiàn)規(guī)范,因為它僅僅是個接口,具體到每個web應(yīng)用服務(wù)器的提供商,除了對規(guī)范支持之外,仍然會有一些規(guī)范里沒有規(guī)定的細微差異。這里我們以BEA的Weblogic Server8.1作為例子來演示。
首先,Weblogic Server提供了一系列的參數(shù)來控制它的HttpSession的實現(xiàn),包括使用cookie的開關(guān)選項,使用URL重寫的開關(guān)選項,session持久化的設(shè)置,session失效時間的設(shè)置,以及針對cookie的各種設(shè)置,比如設(shè)置cookie的名字、路徑、域,cookie的生存時間等。
一般情況下,session都是存儲在內(nèi)存里,當(dāng)服務(wù)器進程被停止或者重啟的時候,內(nèi)存里的session也會被清空,如果設(shè)置了session的持久化特性,服務(wù)器就會把session保存到硬盤上,當(dāng)服務(wù)器進程重新啟動或這些信息將能夠被再次使用,Weblogic Server支持的持久性方式包括文件、數(shù)據(jù)庫、客戶端cookie保存和復(fù)制。
復(fù)制嚴格說來不算持久化保存,因為session實際上還是保存在內(nèi)存里,不過同樣的信息被復(fù)制到各個cluster內(nèi)的服務(wù)器進程中,這樣即使某個服務(wù)器進程停止工作也仍然可以從其他進程中取得session。
cookie生存時間的設(shè)置則會影響瀏覽器生成的cookie是否是一個會話cookie。默認是使用會話cookie。有興趣的可以用它來試驗我們在第四節(jié)里提到的那個誤解。
cookie的路徑對于web應(yīng)用程序來說是一個非常重要的選項,Weblogic Server對這個選項的默認處理方式使得它與其他服務(wù)器有明顯的區(qū)別。后面我們會專題討論。
關(guān)于session的設(shè)置參考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
六、HttpSession常見問題
(在本小節(jié)中session的含義為⑤和⑥的混合)
1、session在何時被創(chuàng)建
一個常見的誤解是以為session在有客戶端訪問時就被創(chuàng)建,然而事實是直到某server端程序調(diào)用HttpServletRequest.getSession(true)這樣的語句時才被創(chuàng)建,注意如果JSP沒有顯示的使用 <%@page session="false"%> 關(guān)閉session,則JSP文件在編譯成Servlet時將會自動加上這樣一條語句HttpSession session = HttpServletRequest.getSession(true);這也是JSP中隱含的session對象的來歷。
由于session會消耗內(nèi)存資源,因此,如果不打算使用session,應(yīng)該在所有的JSP中關(guān)閉它。
2、session何時被刪除
綜合前面的討論,session在下列情況下被刪除a.程序調(diào)用HttpSession.invalidate();或b.距離上一次收到客戶端發(fā)送的session id時間間隔超過了session的超時設(shè)置;或c.服務(wù)器進程被停止(非持久session)
3、如何做到在瀏覽器關(guān)閉時刪除session
嚴格的講,做不到這一點。可以做一點努力的辦法是在所有的客戶端頁面里使用javascript代碼window.oncolose來監(jiān)視瀏覽器的關(guān)閉動作,然后向服務(wù)器發(fā)送一個請求來刪除session。但是對于瀏覽器崩潰或者強行殺死進程這些非常規(guī)手段仍然無能為力。
4、有個HttpSessionListener是怎么回事
你可以創(chuàng)建這樣的listener去監(jiān)控session的創(chuàng)建和銷毀事件,使得在發(fā)生這樣的事件時你可以做一些相應(yīng)的工作。注意是session的創(chuàng)建和銷毀動作觸發(fā)listener,而不是相反。類似的與HttpSession有關(guān)的listener還有HttpSessionBindingListener,HttpSessionActivationListener和HttpSessionAttributeListener。
5、存放在session中的對象必須是可序列化的嗎
不是必需的。要求對象可序列化只是為了session能夠在集群中被復(fù)制或者能夠持久保存或者在必要時server能夠暫時把session交換出內(nèi)存。在Weblogic Server的session中放置一個不可序列化的對象在控制臺上會收到一個警告。我所用過的某個iPlanet版本如果session中有不可序列化的對象,在session銷毀時會有一個Exception,很奇怪。
6、如何才能正確的應(yīng)付客戶端禁止cookie的可能性
對所有的URL使用URL重寫,包括超鏈接,form的action,和重定向的URL,具體做法參見[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
7、開兩個瀏覽器窗口訪問應(yīng)用程序會使用同一個session還是不同的session
參見第三小節(jié)對cookie的討論,對session來說是只認id不認人,因此不同的瀏覽器,不同的窗口打開方式以及不同的cookie存儲方式都會對這個問題的答案有影響。
8、如何防止用戶打開兩個瀏覽器窗口操作導(dǎo)致的session混亂
這個問題與防止表單多次提交是類似的,可以通過設(shè)置客戶端的令牌來解決。就是在服務(wù)器每次生成一個不同的id返回給客戶端,同時保存在session里,客戶端提交表單時必須把這個id也返回服務(wù)器,程序首先比較返回的id與保存在session里的值是否一致,如果不一致則說明本次操作已經(jīng)被提交過了。可以參看《J2EE核心模式》關(guān)于表示層模式的部分。需要注意的是對于使用javascript window.open打開的窗口,一般不設(shè)置這個id,或者使用單獨的id,以防主窗口無法操作,建議不要再window.open打開的窗口里做修改操作,這樣就可以不用設(shè)置。
9、為什么在Weblogic Server中改變session的值后要重新調(diào)用一次session.setValue
做這個動作主要是為了在集群環(huán)境中提示W(wǎng)eblogic Server session中的值發(fā)生了改變,需要向其他服務(wù)器進程復(fù)制新的session值。
10、為什么session不見了
排除session正常失效的因素之外,服務(wù)器本身的可能性應(yīng)該是微乎其微的,雖然筆者在iPlanet6SP1加若干補丁的Solaris版本上倒也遇到過;瀏覽器插件的可能性次之,筆者也遇到過3721插件造成的問題;理論上防火墻或者代理服務(wù)器在cookie處理上也有可能會出現(xiàn)問題。
出現(xiàn)這一問題的大部分原因都是程序的錯誤,最常見的就是在一個應(yīng)用程序中去訪問另外一個應(yīng)用程序。我們在下一節(jié)討論這個問題。
七、跨應(yīng)用程序的session共享
常常有這樣的情況,一個大項目被分割成若干小項目開發(fā),為了能夠互不干擾,要求每個小項目作為一個單獨的web應(yīng)用程序開發(fā),可是到了最后突然發(fā)現(xiàn)某幾個小項目之間需要共享一些信息,或者想使用session來實現(xiàn)SSO(single sign on),在session中保存login的用戶信息,最自然的要求是應(yīng)用程序間能夠訪問彼此的session。
然而按照Servlet規(guī)范,session的作用范圍應(yīng)該僅僅限于當(dāng)前應(yīng)用程序下,不同的應(yīng)用程序之間是不能夠互相訪問對方的session的。各個應(yīng)用服務(wù)器從實際效果上都遵守了這一規(guī)范,但是實現(xiàn)的細節(jié)卻可能各有不同,因此解決跨應(yīng)用程序session共享的方法也各不相同。
首先來看一下Tomcat是如何實現(xiàn)web應(yīng)用程序之間session的隔離的,從Tomcat設(shè)置的cookie路徑來看,它對不同的應(yīng)用程序設(shè)置的cookie路徑是不同的,這樣不同的應(yīng)用程序所用的session id是不同的,因此即使在同一個瀏覽器窗口里訪問不同的應(yīng)用程序,發(fā)送給服務(wù)器的session id也可以是不同的。
根據(jù)這個特性,我們可以推測Tomcat中session的內(nèi)存結(jié)構(gòu)大致如下。
筆者以前用過的iPlanet也采用的是同樣的方式,估計SunONE與iPlanet之間不會有太大的差別。對于這種方式的服務(wù)器,解決的思路很簡單,實際實行起來也不難。要么讓所有的應(yīng)用程序共享一個session id,要么讓應(yīng)用程序能夠獲得其他應(yīng)用程序的session id。
iPlanet中有一種很簡單的方法來實現(xiàn)共享一個session id,那就是把各個應(yīng)用程序的cookie路徑都設(shè)為/(實際上應(yīng)該是/NASApp,對于應(yīng)用程序來講它的作用相當(dāng)于根)。
<session-info>
<path>/NASApp</path>
</session-info>
需要注意的是,操作共享的session應(yīng)該遵循一些編程約定,比如在session attribute名字的前面加上應(yīng)用程序的前綴,使得setAttribute("name", "neo")變成setAttribute("app1.name", "neo"),以防止命名空間沖突,導(dǎo)致互相覆蓋。
在Tomcat中則沒有這么方便的選擇。在Tomcat版本3上,我們還可以有一些手段來共享session。對于版本4以上的Tomcat,目前筆者尚未發(fā)現(xiàn)簡單的辦法。只能借助于第三方的力量,比如使用文件、數(shù)據(jù)庫、JMS或者客戶端cookie,URL參數(shù)或者隱藏字段等手段。
我們再看一下Weblogic Server是如何處理session的。
從截屏畫面上可以看到Weblogic Server對所有的應(yīng)用程序設(shè)置的cookie的路徑都是/,這是不是意味著在Weblogic Server中默認的就可以共享session了呢?然而一個小實驗即可證明即使不同的應(yīng)用程序使用的是同一個session,各個應(yīng)用程序仍然只能訪問自己所設(shè)置的那些屬性。這說明Weblogic Server中的session的內(nèi)存結(jié)構(gòu)可能如下
對于這樣一種結(jié)構(gòu),在session機制本身上來解決session共享的問題應(yīng)該是不可能的了。除了借助于第三方的力量,比如使用文件、數(shù)據(jù)庫、JMS或者客戶端cookie,URL參數(shù)或者隱藏字段等手段,還有一種較為方便的做法,就是把一個應(yīng)用程序的session放到ServletContext中,這樣另外一個應(yīng)用程序就可以從ServletContext中取得前一個應(yīng)用程序的引用。示例代碼如下,
應(yīng)用程序A
context.setAttribute("appA", session);
應(yīng)用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");
值得注意的是這種用法不可移植,因為根據(jù)ServletContext的JavaDoc,應(yīng)用服務(wù)器可以處于安全的原因?qū)τ赾ontext.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通過。
那么Weblogic Server為什么要把所有的應(yīng)用程序的cookie路徑都設(shè)為/呢?原來是為了SSO,凡是共享這個session的應(yīng)用程序都可以共享認證的信息。一個簡單的實驗就可以證明這一點,修改首先登錄的那個應(yīng)用程序的描述符weblogic.xml,把cookie路徑修改為/appA訪問另外一個應(yīng)用程序會重新要求登錄,即使是反過來,先訪問cookie路徑為/的應(yīng)用程序,再訪問修改過路徑的這個,雖然不再提示登錄,但是登錄的用戶信息也會丟失。注意做這個實驗時認證方式應(yīng)該使用FORM,因為瀏覽器和web服務(wù)器對basic認證方式有其他的處理方式,第二次請求的認證不是通過session來實現(xiàn)的。具體請參看[7] secion 14.8 Authorization,你可以修改所附的示例程序來做這些試驗。
八、總結(jié)
session機制本身并不復(fù)雜,然而其實現(xiàn)和配置上的靈活性卻使得具體情況復(fù)雜多變。這也要求我們不能把僅僅某一次的經(jīng)驗或者某一個瀏覽器,服務(wù)器的經(jīng)驗當(dāng)作普遍適用的經(jīng)驗,而是始終需要具體情況具體分析。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>文件上載</title>
<script>
//可以上傳的文件類形
function filetype(){
? ?var sTheFile = document.forms[0].pic.value;
?var? filename? = document.forms[0].filename;
??var sFileName = sTheFile.substring(sTheFile.lastIndexOf("\\") + 1);
?var Name=sFileName.substring(sFileName.indexOf(".")+1).toLowerCase();
?var ext = new Array('jpg','jpeg','png','gif','bmp','ico');
?for (var i=0 ;i<ext.length; i++){
??if( Name == ext[i]){
??filename.value = sFileName;
??//alert(filename.value)
??return true;
??}
?}
?alert("上傳文件格式不正確!");
?document.getElementById("div1").innerHTML='<input type="file" name="pic" id="pic" value="" onChange="filetype()">'
?return false;
?
}
//驗證表單
function checkValue(){
?var sJbr = document.forms[0].jbr.value;
?var sSelect = document.forms[0].select.options(document.forms[0].select.selectedIndex).value;
?var sContent = document.forms[0].content.value;
?var sTheFile = document.forms[0].pic.value;
?if (sJbr =="" ||sJbr == null){
?alert("請?zhí)顚懪e報人!");
?return? false;
?}
?if (sSelect =="" ||sSelect == null){
?alert("請選擇分局!");
?return? false;
?}
?if (sContent =="" ||sContent == null){
?alert("請?zhí)顚懭毕荩?);
?return? false;
?}
?if (sTheFile =="" ||sTheFile == null){
?alert("缺陷截圖不能為空!");
?return? false;
?}
?
?
?
}
</script>
?
</head>
<body>
<form action="/report/servlet/BugFeed" onSubmit="return checkValue()" enctype="MULTIPART/FORM-DATA" method=post>
? <p>舉報人:
??? <input type="text" name="jbr" onBlur="checkValue()">
</p>
? <p>分局:
??? <select name="select">
??????? <option value="">請選擇分局</option>
??????? <option value="城東局">城東局</option>
??????? <option value="城南局">城南局</option>
??????? <option value="城西局">城西局</option>
??????? <option value="城北局">城北局</option>
??<option value="其它">其它</option>
????? </select>
? </p>
? <p>描述缺陷:?
??? <textarea name="content"></textarea>
? </p>
? <p>選擇要上載的圖片:
??? <div id="div1"><input type="file" name="pic" id="pic" value="" onChange="filetype()"></div>
? </p>
? <input type="hidden" value="" name="filename" id="filename">
? <p>
??? <input type=submit value="Upload">
???
????????? </p>
</form>
</body>
</html>
- <!--$Header:?ProcessFileUpload.jsp?1.0.0?2004/10/22?15:10:19?pkm?ship??$-->
- <%@?page?contentType="text/html;charset=GB2312"%>
- <%@?page?import="org.apache.commons.fileupload.DiskFileUpload"%>
- <%@?page?import="org.apache.commons.fileupload.FileItem"%>
- <%@?page?import="java.util.*"%>
- <%@?page?import="java.io.File"%>
- <%@?page?import="java.sql.*"?%>
- <html>
- <head>
- <style>
- .NButton
- {
- ????cursor:hand;
- ????width:?87px;
- ????height:?20px;
- ????font-family:?宋體;
- ????font-size:?12px;
- ????text-align:center?;
- ????background-image:?url(btn_bkg.gif);
- ????border:0px;
- }
- </style>
- <%!
- //?Connect?to?Oracle?database?and?Insert?into?cux_upload_files
- public ?void?dbInsert(String?p_c_file_name,String?p_c_path,String?p_s_file_name,String?p_s_path)?{
- ????Connection?conn?=?null;
- ????String?connStr;
- ??try?{
- ????connStr="jdbc:oracle:thin:@local:1521:orcl";
- ????DriverManager.registerDriver(new?oracle.jdbc.driver.OracleDriver());
- ????conn?=?DriverManager.getConnection(connStr,"apps",?"apps");
- ????//?Insert?into?table
- ????conn.setAutoCommit(false);?
- ????PreparedStatement?insertCUF?=?conn.prepareStatement("INSERT?INTO?cux.cux_upload_files(file_id,client_file_name,client_path,server_file_name,server_path,created_by,creation_date)?"
- ????+?"?VALUES?(cux.cux_upload_files_s.nextval,?,?,?,?,?,SYSDATE)?");
- ??
- ????//insertCUF.setInt(1,2);
- ????insertCUF.setString(1,p_c_file_name);
- ????insertCUF.setString(2,p_c_path);
- ????insertCUF.setString(3,p_s_file_name);
- ????insertCUF.setString(4,p_s_path);
- ????insertCUF.setString(5,"XXX");
- ????insertCUF.executeUpdate();
- ??
- ????conn.commit();
- ??
- ????conn.setAutoCommit(true);
- ??
- ????conn.close();
- ??}catch(SQLException?ex)?{?//?Handle?SQL?errors
- ??????System.out.println("Error?in?Connecting?to?the?Database?"+'\n'+ex.toString());
- ??}
- }
- //
- String ?getCurDate(){
- ????GregorianCalendar?gcDate?=?new?GregorianCalendar();?
- ????int?year??=?gcDate.get(GregorianCalendar.YEAR);
- ????int?month?=?gcDate.get(GregorianCalendar.MONTH);
- ????int?day???=?gcDate.get(GregorianCalendar.DAY_OF_MONTH);
- ????return?""?+?year?+?"-"?+?month?+?"-"?+?day;
- }
- %>
- <meta?http-equiv="Content-Type"?content="text/html;?charset=gb2312"?>
- <title>Process?File?Upload</title>
- </head>
- <body>
- <table?width="800"?border?bordercolor="#0000FF">
- <tr?bgcolor="#66CCFF">
- <td?colspan=1?rowspan=1?align=left?valign=top>
- ????<strong><font?size=2?face="宋體"?color=#000000>
- ????<nobr>客戶端文件</nobr>
- ????</font>
- ????</strong></td>
- <td?colspan=1?rowspan=1?align=left?valign=top>
- ????<strong><font?size=2?face="宋體"?color=#000000>
- ????<nobr>服務(wù)器文件</nobr>
- ????</font>
- ????</strong></td>
- <td?colspan=1?rowspan=1?align=left?valign=top>
- ????<strong><font?size=2?face="宋體"?color=#000000>
- ????<nobr>上傳用戶</nobr>
- ????</font>
- ????</strong></td>
- </tr>
- <%
- ????//out.println("Content?Type?="+request.getContentType());
- ????
- ????DiskFileUpload?fu?=?new?DiskFileUpload();
- ????//?If?file?size?exceeds,?a?FileUploadException?will?be?thrown
- ????fu.setSizeMax(1000000);
- ????//?maximum?size?that?will?be?stored?in?memory
- ????fu.setSizeThreshold(4096);
- ????//?the?location?for?saving?data?that?is?larger?than?getSizeThreshold()
- ????//fu.setRepositoryPath("/tmp/");
- ????
- ????
- ????List?fileItems?=?fu.parseRequest(request);
- ????Iterator?itr?=?fileItems.iterator();
- ????int?i?=?0;
- ??
- ????while(itr.hasNext())?{
- ??????FileItem?fi?=?(FileItem)itr.next();
- ??????i++;
- ??????//Check?if?not?form?field?so?as?to?only?handle?the?file?inputs
- ??????//else?condition?handles?the?submit?button?input
- ??????if?(!fi.isFormField())?{
- ????????????String?filename?=?fi.getName();
- ????????????long?filesize?=?fi.getSize();
- ??????String?pUserName?=?"XIAOHUIPING";
- ????????????if((filename==null||filename.equals(""))?&&?filesize==0)
- ????????????continue;
- ????????????//?注意fi.getName()
- ????????????//?會返回上載文件在客戶端的完整路徑名稱,這似乎是一個BUG。
- ????????????//?為解決這個問題,這里使用了fullFile.getName()。
- ????????????filename=filename.replace('\\','/');
- ????????????//new?String(filename.getBytes("ISO-8859-1"),"UTF-8");
- ????????????File?fullFile?=?new?File(filename);
- ????????????//?指定fullFile.getName()?=?"Works.txt";
- ????????????File?savedFile=?new?File(application.getRealPath("/Download/"),?fullFile.getName());
- ????????????fi.write(savedFile);
- ????????????//?上傳文件成功后寫入數(shù)據(jù)庫表
- ????????????dbInsert(fullFile.getName(),filename.replace('/','\\'),fullFile.getName(),savedFile.getAbsolutePath());
- ??????if?((i%2)?==?0)?{
- ????????????//?文件上載成功提示,以表格形式打印
- ??????out.println("<tr?bgcolor=\"#CCCCCC\"><td?colspan=1?rowspan=1?align=left?valign=top><font?size=2?face=\"宋體\"?color=#000000><nobr>"?+?fullFile.getName()?+?"</nobr></font></td><td?colspan=1?rowspan=1?align=left?valign=top><font?size=2?face=\"宋體\"?color=#000000><nobr>"?+?fullFile.getName()?+?"</nobr></font></td><td?colspan=1?rowspan=1?align=left?valign=top><font?size=2?face=\"宋體\"?color=#000000><nobr>"?+?pUserName?+?"</nobr></font></td></tr>");
- ????????????//out.println("<br>"?+?"Local?Filename?=?"?+?"\""?+?filename.replace('/','\\')?+?"\""?+?"?Upload?To?\""??+?savedFile.getAbsolutePath()?+?"\""?+?"?Successful!!");
- ????????????}
- ??????else?if?((i%2)?==?1)?{
- ??????out.println("<tr><td?colspan=1?rowspan=1?bgcolor=#ffffff?align=left?valign=top><font?size=2?face=\"宋體\"?color=#000000><nobr>"?+?fullFile.getName()?+?"</nobr></font></td><td?colspan=1?rowspan=1?bgcolor=#ffffff?align=left?valign=top><font?size=2?face=\"宋體\"?color=#000000><nobr>"?+?fullFile.getName()?+?"</nobr></font></td><td?colspan=1?rowspan=1?bgcolor=#ffffff?align=left?valign=top><font?size=2?face=\"宋體\"?color=#000000><nobr>"?+?pUserName?+?"</nobr></font></td></tr>");
- ??????}
- ??????}
- ????}
- %>
- </table>
- <table?width="800"?border?bordercolor="#0000FF">
- ??<tr>
- ????<td?height="20"?align="center"?nowrap="nowrap">
- ??????<DIV?align="center">
- ????????<input?class="NButton"?type="button"?value="Back"?onClick="javascript:history.back()"/>
- ????????<input?class="NButton"?type="button"?value="Close"?onClick="javascript:window.close()"/>
- ??????</DIV>
- ????</td>
- ??</tr>
- </table>
- </body>
- </html>
- <!--$Header:?index.jsp?1.0.0?2004/10/22?15:10:19?pkm?ship??$-->
- <%@?page?contentType?=?"text/html;charset=gb2312"?%>
- <html>
- <head>
- <title>數(shù)據(jù)文件上傳</title>
- <style>
- BODY
- {
- ??FONT-FAMILY:?宋體;
- ??FONT-SIZE:?10pt;
- ????background-color:?#F6F6F6;
- ????margin-top:?10px;
- ????margin-right:?50px;
- ????margin-bottom:?50px;
- ????margin-left:?10px;
- ????margin-top:?0px
- ????SCROLLBAR-FACE-COLOR:?#D0E5FF;
- ????SCROLLBAR-HIGHLIGHT-COLOR:?#F5F9FF;
- ????SCROLLBAR-SHADOW-COLOR:?#828282;
- ????SCROLLBAR-3DLIGHT-COLOR:?#828282;
- ????SCROLLBAR-ARROW-COLOR:?#797979;
- ????SCROLLBAR-TRACK-COLOR:?#ECECEC;
- ????SCROLLBAR-DARKSHADOW-COLOR:?#ffffff
- }
- TABLE
- {
- ??FONT-FAMILY:?宋體;
- ??FONT-SIZE:?10pt
- }
- .HeaderTitle{
- ????font-family:?黑體;
- ????font-size:?30px;
- ????font-weight:?bolder;
- ????color:?#041986;
- }
- .TitleBar
- {
- ??BACKGROUND-COLOR:?#E5EAED;
- ??Color:#565656;
- ??FONT-FAMILY:?宋體;
- ??font-weight:bold;
- ??FONT-SIZE:?11pt;
- }
- .TextBox
- {
- ????FONT-FAMILY:?宋體;
- ????FONT-SIZE:?10pt;
- ????height:?20px;
- ????BORDER-BOTTOM:?1pt?solid?#C6C6C6;
- ????BORDER-LEFT:?1pt?solid?#C6C6C6;
- ????BORDER-RIGHT:?1pt?solid?#C6C6C6;
- ????BORDER-TOP:?1pt?solid?#C6C6C6;
- }
- .InputGridTable{
- ??FONT-FAMILY:?宋體;
- ??FONT-SIZE:?10pt;
- ????border-collapse:?collapse;
- ????border-color:#C6C6C6;
- ????border-style:?solid;
- ????border-width:?1;
- ????padding:?0;
- }
- .TitleColumn{
- ????background-color:?#E8ECF0;
- ????nowrap="nowrap";
- ??HEIGHT:?20px
- }
- .NButton
- {
- ????cursor:hand;
- ????width:?87px;
- ????height:?20px;
- ????font-family:?宋體;
- ????font-size:?12px;
- ????text-align:center?;
- ????background-image:?url(btn_bkg.gif);
- ????border:0px;
- }
- </style>
- <script?language="javascript">
- var?count?=?1;
- function?delAttacheFile(){
- ????var?targetRow?=?event.srcElement.parentElement.parentElement;
- ????InputTable.deleteRow(targetRow.rowIndex);
- }
- function?addAttacheFile(){
- ????count?++;
- ????var?row?=?InputTable.insertRow(InputTable.rows.length);
- ????var?firstCell?=?row.insertCell(0);
- ????firstCell.className?=?"TitleColumn";
- ????firstCell.width?=?"10%";
- ????firstCell.height?=?"20";
- ????firstCell.innerHTML?=?"<strong>?附件?"?+?count+?"?:</strong>";
- ????var?lastCell?=?row.insertCell(1);
- ????lastCell.height?=?"20";
- ????lastCell.innerHTML?=?"<input?type='file'?name='attacheFile"?+?count?+?"'?size='50'?class='TextBox'>?<input?type='button'?value='刪除附件?"?+?count?+?"'?onclick='delAttacheFile();'?class='NButton'>";
- }
- </script>
- </head>
- <body>
- ??<form?name="filesForm"?method="POST"?action="ProcessFileUpload.jsp"?enctype="multipart/form-data">
- ????<table?id="InputTable"?border="1"?cellpadding="0"?cellspacing="0"?class="InputGridTable"?width="100%"?height="40"?>
- ??????<tr>
- ????????<td?nowrap="nowrap"?height="20"?colspan="3"?class="TitleColumn"><div?align="center"><strong>附件列表:</strong></div></td>
- ??????</tr>
- ??????<tr>
- ????????<td?nowrap="nowrap"?width="10%"?height="20"?class="TitleColumn"><strong>附件?1?:</strong></td>
- ????????<td?height="20"?nowrap="nowrap"><input?type="file"?class="TextBox"?name="attacheFile1"?size="50">?<input?name="adfile"?type="button"?class="NButton"?onClick="addAttacheFile();"?value="添加附件"></td>
- ??????</tr>
- ????</table>
- ????<table?id="SubmitTable"?border="1"?cellpadding="0"?cellspacing="0"?class="InputGridTable"?width="100%"?height="20"?>
- ??????<tr>
- ????????<td?height="20"?align="center"?nowrap="nowrap">
- ??????????<input?type="submit"?name="close"?value="Close"?onClick="self.close();"?class="NButton"/>
- ??????????<input?type="reset"?name="reset"?value="Clear"?class="NButton"/>
- ??????????<input?type="submit"?name="Submit"?value="Upload"?class="NButton"/>?
- ????????</td>
- ??????</tr>
- ????</table>
- ??</form>
- ????<p>?</p>
- </body>
- </html>
import java.net.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SaveFileServlet extends HttpServlet
{
?? FileWriter savefile;
?? String filename = null;
?? String value = null;
?? /**
?? * Handles a POST request
?? */
?? public void doPost(
???????? HttpServletRequest request,
???????? HttpServletResponse response)
????? throws ServletException, IOException
?? {
????? PrintWriter out = response.getWriter();
????? response.setContentType("text/html");
????? //FileWriter savefile;
????? try {
???????? // Verify the content type
???????? String ct = request.getContentType();
???????? if (!ct.startsWith("multipart/form-data"))
??????????? throw new RuntimeException
??????????? ("Invalid content type");
???????? // Get the boundary string
???????? int p = ct.indexOf("boundary=");
???????? if (p == -1)
??????????? throw new RuntimeException
??????????? ("No boundary string found");
???????? p += "boundary=".length();
???????? String boundary = "--" + ct.substring(p);
???????? String finalBoundary = boundary + "--";
???????? // We'll parse the multipart/form-data
???????? // with a finite state machine
???????? // Define names for the parser states
???????? final int INIT = 0;
???????? final int READING_HEADERS = 1;
???????? final int READING_DATA = 2;
???????? int state = INIT;
???????? // Read and extract the fields
???????? BufferedReader in = request.getReader();
???????? main: for (;;) {
??????????? String line = in.readLine();
??????????? if (line == null)
?????????????? break;
??????????? switch (state) {
?????????????? // State 0: Ignoring everything before
?????????????? // the first boundary
?????????????? case INIT:
????????????????? if (line.startsWith(finalBoundary))
???????????????????? break main;
????????????????? if (line.startsWith(boundary)) {
???????????????????? state = READING_HEADERS;
???????????????????? filename = "";
???????????????????? value = "";
????????????????? }
????????????????? break;
?????????????? // State 1: Parsing the headers
?????????????? case READING_HEADERS:
????????????????? if (line.length() == 0)
???????????????????? state = READING_DATA;
????????????????? else {
???????????????????? // Get the field name
???????????????????? p = line.indexOf("filename=\"");
???????????????????? if (p == -1)
??????????????????????? break;
???????????????????? p += "filename=\"".length();
???????????????????? // ... up to the closing quote.
???????????????????? int q = line.indexOf("\"", p);
???????????????????? if (q == -1)
??????????????????????? break;
???????????????????? filename = line.substring(p, q);
???????????????????? filename="./config/medet/applications/DefaultWebApp/"+filename.substring(filename.lastIndexOf("\\")+1);
???????????????????? savefile=new FileWriter(filename);
???????????????????? value = "";
????????????????? }
????????????????? break;
?????????????? // State 2: Reading the data
?????????????? case READING_DATA:
????????????????? if (line.startsWith(finalBoundary)) {
???????????????????? savefile.write(value);
???????????????????? savefile.close();
???????????????????? break main;
????????????????? }
????????????????? if (line.startsWith(boundary)) {
???????????????????? state = READING_HEADERS;
????????????????? }
????????????????? else {
???????????????????? if (value.length() > 0)
??????????????????????? value += "\n";
???????????????????? value += line;
????????????????? }
????????????????? break;
??????????? }
???????? }
???????? // Report the incident number back to the client
???????? String[] text = {
??????????? "<HTML>",
??????????? "<HEAD>",
??????????? "<meta http-equiv='Content-Type' content='text/html; charset=gb2312'>",
??????????? "<TITLE>文件上傳成功</TITLE>",
??????????? "</HEAD>",
??????????? "<BODY>",
??????????? "<CENTER>",
??????????? "<H3>文件上傳成功!</H3>",
??????????? "</CENTER>"
???????? };
???????? for (int i = 0; i < text.length; i++)
??????????? out.println(text[i]);
??????????? out.println(filename);
??????????? out.println("</BODY>");
??????????? out.println("</HTML>");
????? }
????? catch (Exception e) {
???????? // Write the exception message
???????? out.println("<H3>Error:</H3>");
???????? out.println("<PRE>");
???????? out.println(e.getMessage());
???????? out.println("</PRE>");
????? }
????? finally {
???????? out.flush();
????? }
?? }
}
Class文件如下:
import java.io.*;
import java.sql.*;
import java.util.Date ;
import oracle.sql.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import org.apache.struts.upload.*;
public class UploadAction extends Action
{
? public ActionForward execute(ActionMapping mapping,
?????????????????????????????? ActionForm form,
?????????????????????????????? HttpServletRequest request,
?????????????????????????????? HttpServletResponse response)
????? throws Exception {
??? if (form instanceof UploadForm) {
????? Date date = new Date();
????? System.out.println("-------------File Upload Begins-------------------") ;
????? UploadForm theForm = (UploadForm) form;
????? file://通/過struts的FormFile類來獲得上傳的文件,前臺jsp頁面對應(yīng)的代碼
????? file://</html:file property="theFile" />
????? FormFile file = theForm.getTheFile();
?????
????? Connection conn = null;
????? PreparedStatement ps = null ;
????? String union_Id = "" ;
????? String union_Version = "" ;
????? union_Id = theForm.getUnion_Id() ;
????? union_Version = theForm.getUnion_Version() ;
????? file://actionpart/是數(shù)據(jù)庫表中的字段名,由于表中有多個blob字段,所以用變量來表示其名稱
????? String actionpart = theForm.getActionpart() ;
????? System.out.println("actionpart is:"+actionpart) ;
????? file://取/得數(shù)據(jù)庫連接,dbPool的源代碼附在后面
????? dbPool dbp = new dbPool();
????? conn = dbp.getConnection() ;
????? ResultSet BlobDetails = null;
????? Statement stmt = null;
????? try {
??????? InputStream stream = null;
??????? FileInputStream fstream = null;
??????? stream = file.getInputStream();
??????? System.out.println("------------uploadFileSize is : "+stream.available() +"------------") ;
??????? conn.setAutoCommit(false);
??????? stmt = conn.createStatement();
??????? file://先/用empty_blob()來初始化該字段
??????? sql = " update regunion set " + actionpart + "? = empty_blob() where? union_id= " + union_Id +" and? union_Version = "+ union_Version;
??????? stmt.executeUpdate(sql) ;
??????? conn.commit() ;
??????? stmt.close() ;
??????? stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
??????? sql = "select " +actionpart + " from regunion where union_id= " + union_Id +" and? union_Version = "+ union_Version + " for? update ";
??????? BlobDetails = stmt.executeQuery(sql);
??????? if (BlobDetails.next()) {
????????? BLOB l_mapBlob = (BLOB)BlobDetails.getBlob(1);
????????? OutputStream l_blobOutputStream = ((BLOB) l_mapBlob).getBinaryOutputStream();
????????? byte[] l_buffer = new byte[10* 1024];
????????? int l_nread = 0;
????????? while ((l_nread=stream.read(l_buffer)) != -1) // Read from file
??????????? {
??????????? l_blobOutputStream.write(l_buffer,0,l_nread); // Write to Blob
??????????? }
????????? stream.close();
????????? l_blobOutputStream.close ();
??????? }
??????? BlobDetails.close();
??????? conn.commit() ;
????? }
????? catch (FileNotFoundException fnfe) {
??????? fnfe.printStackTrace() ;
??????? return mapping.findForward("error");
????? }
????? catch (IOException ioe) {
??????? ioe.printStackTrace() ;
??????? return mapping.findForward("error");;
????? }
????? catch(SQLException ex){
??????? ex.printStackTrace() ;
??????? return mapping.findForward("error");;
????? }finally{
??????? if(conn != null){
????????? try{
??????????? stmt.close();
??????????? conn.close() ;
????????? }catch(Exception sqle){
??????????? sqle.printStackTrace() ;
????????? }
??????? }
????? }
????? request.setAttribute("union_Id",union_Id) ;
????? request.setAttribute("union_Version",union_Version) ;
????? System.out.println("-------------File Upload Ends-------------------") ;
????? return mapping.findForward("success");
??? }else{
????? return null;
??? }
? }
}
附:dbPool.java程序
package com.prient.nbsc.unifylaw;
import java.sql.*;
import com.prient.nbsc.common.DBPOOL;
public class dbPool {
? public Connection getConnection(){
??? try {
????? Connection conn = null;
????? Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
????? String bridge = "jdbc:oracle:thin:@10.6.89.2:1521:sjk";
????? conn = DriverManager.getConnection(bridge,"user","user") ;
????? return? conn;
??? }
??? catch (Exception e) {
????? System.out.println(e);
????? return null;
?????? }
? }
}
JAVA EXCEL API簡介
??? Java Excel是一開放源碼項目,通過它Java開發(fā)人員可以讀取Excel文件的內(nèi)容、創(chuàng)建新的Excel文件、更新已經(jīng)存在的Excel文件。使用該API非Windows操作系統(tǒng)也可以通過純Java應(yīng)用來處理Excel數(shù)據(jù)表。因為是使用Java編寫的,所以我們在Web應(yīng)用中可以通過JSP、Servlet來調(diào)用API實現(xiàn)對Excel數(shù)據(jù)表的訪問。
現(xiàn)在發(fā)布的穩(wěn)定版本是V2.0,提供以下功能:
從Excel 95、97、2000等格式的文件中讀取數(shù)據(jù);
讀取Excel公式(可以讀取Excel 97以后的公式);
生成Excel數(shù)據(jù)表(格式為Excel 97);
支持字體、數(shù)字、日期的格式化;
支持單元格的陰影操作,以及顏色操作;
修改已經(jīng)存在的數(shù)據(jù)表;
現(xiàn)在還不支持以下功能,但不久就會提供了:
不能夠讀取圖表信息;
可以讀,但是不能生成公式,任何類型公式最后的計算值都可以讀出;
應(yīng)用示例
1 從Excel文件讀取數(shù)據(jù)表
Java Excel API既可以從本地文件系統(tǒng)的一個文件(.xls),也可以從輸入流中讀取Excel數(shù)據(jù)表。讀取Excel數(shù)據(jù)表的第一步是創(chuàng)建Workbook(術(shù)語:工作薄),下面的代碼片段舉例說明了應(yīng)該如何操作:(完整代碼見ExcelReading.java)
import java.io.*;
import jxl.*;
… … … …
try
{
//構(gòu)建Workbook對象, 只讀Workbook對象
//直接從本地文件創(chuàng)建Workbook
//從輸入流創(chuàng)建Workbook
InputStream is = new FileInputStream(sourcefile);
jxl.Workbook rwb = Workbook.getWorkbook(is);
}
catch (Exception e)
{
e.printStackTrace();
}
一旦創(chuàng)建了Workbook,我們就可以通過它來訪問Excel Sheet(術(shù)語:工作表)。參考下面的代碼片段:
//獲取第一張Sheet表
Sheet rs = rwb.getSheet(0);
我們既可能通過Sheet的名稱來訪問它,也可以通過下標(biāo)來訪問它。如果通過下標(biāo)來訪問的話,要注意的一點是下標(biāo)從0開始,就像數(shù)組一樣。
一旦得到了Sheet,我們就可以通過它來訪問Excel Cell(術(shù)語:單元格)。參考下面的代碼片段:
//獲取第一行,第一列的值
Cell c00 = rs.getCell(0, 0);
String strc00 = c00.getContents();
//獲取第一行,第二列的值
Cell c10 = rs.getCell(1, 0);
String strc10 = c10.getContents();
//獲取第二行,第二列的值
Cell c11 = rs.getCell(1, 1);
String strc11 = c11.getContents();
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
如果僅僅是取得Cell的值,我們可以方便地通過getContents()方法,它可以將任何類型的Cell值都作為一個字符串返回。示例代碼中Cell(0, 0)是文本型,Cell(1, 0)是數(shù)字型,Cell(1,1)是日期型,通過getContents(),三種類型的返回值都是字符型。
如果有需要知道Cell內(nèi)容的確切類型,API也提供了一系列的方法。參考下面的代碼片段:
String strc00 = null;
double strc10 = 0.00;
Date strc11 = null;
Cell c00 = rs.getCell(0, 0);
Cell c10 = rs.getCell(1, 0);
Cell c11 = rs.getCell(1, 1);
if(c00.getType() == CellType.LABEL)
{
LabelCell labelc00 = (LabelCell)c00;
strc00 = labelc00.getString();
}
if(c10.getType() == CellType.NUMBER)
{
NmberCell numc10 = (NumberCell)c10;
strc10 = numc10.getValue();
}
if(c11.getType() == CellType.DATE)
{
DateCell datec11 = (DateCell)c11;
strc11 = datec11.getDate();
}
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
在得到Cell對象后,通過getType()方法可以獲得該單元格的類型,然后與API提供的基本類型相匹配,強制轉(zhuǎn)換成相應(yīng)的類型,最后調(diào)用相應(yīng)的取值方法getXXX(),就可以得到確定類型的值。API提供了以下基本類型,與Excel的數(shù)據(jù)格式相對應(yīng),如下圖所示:
每種類型的具體意義,請參見Java Excel API Document。
當(dāng)你完成對Excel電子表格數(shù)據(jù)的處理后,一定要使用close()方法來關(guān)閉先前創(chuàng)建的對象,以釋放讀取數(shù)據(jù)表的過程中所占用的內(nèi)存空間,在讀取大量數(shù)據(jù)時顯得尤為重要。參考如下代碼片段:
//操作完成時,關(guān)閉對象,釋放占用的內(nèi)存空間
rwb.close();
Java Excel API提供了許多訪問Excel數(shù)據(jù)表的方法,在這里我只簡要地介紹幾個常用的方法,其它的方法請參考附錄中的Java Excel API Document。
Workbook類提供的方法
1. int getNumberOfSheets()
獲得工作薄(Workbook)中工作表(Sheet)的個數(shù),示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
int sheets = rwb.getNumberOfSheets();
2. Sheet[] getSheets()
返回工作?。╓orkbook)中工作表(Sheet)對象數(shù)組,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
Sheet[] sheets = rwb.getSheets();
3. String getVersion()
返回正在使用的API的版本號,好像是沒什么太大的作用。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
String apiVersion = rwb.getVersion();
Sheet接口提供的方法
1) String getName()
獲取Sheet的名稱,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
String sheetName = rs.getName();
2) int getColumns()
獲取Sheet表中所包含的總列數(shù),示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsColumns = rs.getColumns();
3) Cell[] getColumn(int column)
獲取某一列的所有單元格,返回的是單元格對象數(shù)組,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getColumn(0);
4) int getRows()
獲取Sheet表中所包含的總行數(shù),示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsRows = rs.getRows();
5) Cell[] getRow(int row)
獲取某一行的所有單元格,返回的是單元格對象數(shù)組,示例子:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getRow(0);
6) Cell getCell(int column, int row)
獲取指定單元格的對象引用,需要注意的是它的兩個參數(shù),第一個是列數(shù),第二個是行數(shù),這與通常的行、列組合有些不同。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell cell = rs.getCell(0, 0);
2 生成新的Excel工作薄
下面的代碼主要是向大家介紹如何生成簡單的Excel工作表,在這里單元格的內(nèi)容是不帶任何修飾的(如:字體,顏色等等),所有的內(nèi)容都作為字符串寫入。(完整代碼見ExcelWriting.java)
與讀取Excel工作表相似,首先要使用Workbook類的工廠方法創(chuàng)建一個可寫入的工作薄(Workbook)對象,這里要注意的是,只能通過API提供的工廠方法來創(chuàng)建Workbook,而不能使用WritableWorkbook的構(gòu)造函數(shù),因為類WritableWorkbook的構(gòu)造函數(shù)為protected類型。示例代碼片段如下:
import java.io.*;
import jxl.*;
import jxl.write.*;
… … … …
try
{
//構(gòu)建Workbook對象, 只讀Workbook對象
//Method 1:創(chuàng)建可寫入的Excel工作薄
jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));
//Method 2:將WritableWorkbook直接寫入到輸出流
/*
OutputStream os = new FileOutputStream(targetfile);
jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
*/
}
catch (Exception e)
{
e.printStackTrace();
}
API提供了兩種方式來處理可寫入的輸出流,一種是直接生成本地文件,如果文件名不帶全路徑的話,缺省的文件會定位在當(dāng)前目錄,如果文件名帶有全路徑的話,則生成的Excel文件則會定位在相應(yīng)的目錄;另外一種是將Excel對象直接寫入到輸出流,例如:用戶通過瀏覽器來訪問Web服務(wù)器,如果HTTP頭設(shè)置正確的話,瀏覽器自動調(diào)用客戶端的Excel應(yīng)用程序,來顯示動態(tài)生成的Excel電子表格。
接下來就是要創(chuàng)建工作表,創(chuàng)建工作表的方法與創(chuàng)建工作薄的方法幾乎一樣,同樣是通過工廠模式方法獲得相應(yīng)的對象,該方法需要兩個參數(shù),一個是工作表的名稱,另一個是工作表在工作薄中的位置,參考下面的代碼片段:
//創(chuàng)建Excel工作表
jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
"這鍋也支好了,材料也準(zhǔn)備齊全了,可以開始下鍋了!",現(xiàn)在要做的只是實例化API所提供的Excel基本數(shù)據(jù)類型,并將它們添加到工作表中就可以了,參考下面的代碼片段:
//1.添加Label對象
jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
ws.addCell(labelC);
//添加帶有字型Formatting的對象
jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
ws.addCell(labelCF);
//添加帶有字體顏色Formatting的對象
jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
Underlinestyle.NO_UNDERLINE, jxl.format.Colour.RED);
jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
ws.addCell(labelCF);
//2.添加Number對象
jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
ws.addCell(labelN);
//添加帶有formatting的Number對象
jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
ws.addCell(labelNF);
//3.添加Boolean對象
jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
ws.addCell(labelB);
//4.添加DateTime對象
jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
ws.addCell(labelDT);
//添加帶有formatting的DateFormat對象
jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
ws.addCell(labelDTF);
這里有兩點大家要引起大家的注意。第一點,在構(gòu)造單元格時,單元格在工作表中的位置就已經(jīng)確定了。一旦創(chuàng)建后,單元格的位置是不能夠變更的,盡管單元格的內(nèi)容是可以改變的。第二點,單元格的定位是按照下面這樣的規(guī)律(column, row),而且下標(biāo)都是從0開始,例如,A1被存儲在(0, 0),B1被存儲在(1, 0)。
最后,不要忘記關(guān)閉打開的Excel工作薄對象,以釋放占用的內(nèi)存,參見下面的代碼片段:
//寫入Exel工作表
wwb.write();
//關(guān)閉Excel工作薄對象
wwb.close();
這可能與讀取Excel文件的操作有少少不同,在關(guān)閉Excel對象之前,你必須要先調(diào)用write()方法,因為先前的操作都是存儲在緩存中的,所以要通過該方法將操作的內(nèi)容保存在文件中。如果你先關(guān)閉了Excel對象,那么只能得到一張空的工作薄了。
3 拷貝、更新Excel工作薄
接下來簡要介紹一下如何更新一個已經(jīng)存在的工作薄,主要是下面二步操作,第一步是構(gòu)造只讀的Excel工作薄,第二步是利用已經(jīng)創(chuàng)建的Excel工作薄創(chuàng)建新的可寫入的Excel工作薄,參考下面的代碼片段:(完整代碼見ExcelModifying.java)
//創(chuàng)建只讀的Excel工作薄的對象
jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));
//創(chuàng)建可寫入的Excel工作薄對象
jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile), rw);
//讀取第一張工作表
jxl.write.WritableSheet ws = wwb.getSheet(0);
//獲得第一個單元格對象
jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
//判斷單元格的類型, 做出相應(yīng)的轉(zhuǎn)化
if(wc.getType() == CellType.LABEL)
{
Label l = (Label)wc;
l.setString("The value has been modified.");
}
//寫入Excel對象
wwb.write();
//關(guān)閉可寫入的Excel對象
wwb.close();
//關(guān)閉只讀的Excel對象
rw.close();
之所以使用這種方式構(gòu)建Excel對象,完全是因為效率的原因,因為上面的示例才是API的主要應(yīng)用。為了提高性能,在讀取工作表時,與數(shù)據(jù)相關(guān)的一些輸出信息,所有的格式信息,如:字體、顏色等等,是不被處理的,因為我們的目的是獲得行數(shù)據(jù)的值,既使沒有了修飾,也不會對行數(shù)據(jù)的值產(chǎn)生什么影響。唯一的不利之處就是,在內(nèi)存中會同時保存兩個同樣的工作表,這樣當(dāng)工作表體積比較大時,會占用相當(dāng)大的內(nèi)存,但現(xiàn)在好像內(nèi)存的大小并不是什么關(guān)鍵因素了。
一旦獲得了可寫入的工作表對象,我們就可以對單元格對象進行更新的操作了,在這里我們不必調(diào)用API提供的add()方法,因為單元格已經(jīng)于工作表當(dāng)中,所以我們只需要調(diào)用相應(yīng)的setXXX()方法,就可以完成更新的操作了。
盡單元格原有的格式化修飾是不能去掉的,我們還是可以將新的單元格修飾加上去,以使單元格的內(nèi)容以不同的形式表現(xiàn)。
新生成的工作表對象是可寫入的,我們除了更新原有的單元格外,還可以添加新的單元格到工作表中,這與示例2的操作是完全一樣的。
最后,不要忘記調(diào)用write()方法,將更新的內(nèi)容寫入到文件中,然后關(guān)閉工作薄對象,這里有兩個工作薄對象要關(guān)閉,一個是只讀的,另外一個是可寫入的。
以上摘自IBM網(wǎng)站
Poi即poor object interface之意,是poi項目組對微軟封閉的office文件格式(接口)的稱謂!
其中提供了對word和excel的java接口,用法非常簡單,并且是完全免費的,對中文的支持也相當(dāng)不錯,下面是處理excel一個簡單的實例:
package test;
import java.io.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
public class Test {
public static void main(String[] args) {
try {
/**************創(chuàng)建一個xls文檔*************/
HSSFWorkbook wb = new HSSFWorkbook();
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short)0);
HSSFCell cell = row.createCell((short)0);
row.createCell((short)1);
row.createCell((short)2);
row.createCell((short)3);
row.createCell((short)4);
row.createCell((short)5);
cell.setCellValue("test_write!");
wb.write(fileOut);
fileOut.close();
/*************讀取并修改xls文檔***************/
POIFSFileSystem pfs=new POIFSFileSystem(new FileInputStream("workbook.xls"));
HSSFWorkbook hwb=new HSSFWorkbook(pfs);
HSSFSheet hs=hwb.getSheetAt(0);
HSSFRow hr=hs.getRow(0);
HSSFCellStyle style=hwb.createCellStyle();
style.setAlignment(style.ALIGN_CENTER);
HSSFFont hf=hwb.createFont();
hf.setFontName("楷體_GB2312");
style.setFont(hf);
HSSFCell cl=hr.getCell((short)2);
cl.setEncoding(cl.ENCODING_UTF_16);
cl.setCellStyle(style);
cl.setCellValue("test_modify! 測試\u2014\u2014編輯!");
FileOutputStream fos=new FileOutputStream("workbook.xls");
hwb.write(fos);
fos.flush();
fos.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}