自寫自看

          commons-fileupload用戶指南(轉(zhuǎn))

          根據(jù)你的應(yīng)用需求,fileupload可以有許多不同的使用方式。在最簡(jiǎn)單的情況下,你可以調(diào)用一個(gè)簡(jiǎn)單的方法來(lái)解析servlet請(qǐng)求,然后在他們提交到你的應(yīng)用時(shí)處理表單列表。在其它規(guī)模的終端上,你也可能決定將fileupload進(jìn)行自定義,以完全控制個(gè)別表單項(xiàng)存儲(chǔ)的方法。比如,你或許會(huì)將內(nèi)容流化來(lái)保存到數(shù)據(jù)中。
          在這里。我們將要描述fileupload的基本使用方法,然后解釋一些更簡(jiǎn)單的,以及最常用的使用模式。fileupload的個(gè)性化在這里得到了描述。

          它怎么工作

          一個(gè)文件上傳請(qǐng)求由有序表單項(xiàng)的列表組成,這些表單是根據(jù)RFC1867來(lái)編碼的,是在html中基于表單的文件上傳。fileupload能解析這樣一個(gè)請(qǐng)求然后向你的應(yīng)用提供一個(gè)單獨(dú)的上傳表單項(xiàng)的列表。每一個(gè)這樣的表單項(xiàng)實(shí)現(xiàn)了FileItem接口,而不需要考慮它潛在的實(shí)現(xiàn)方式。每個(gè)文件的表單項(xiàng)擁有一系列可能對(duì)你的應(yīng)用有用的屬性,比如,每個(gè)項(xiàng)目都有一個(gè)名字和文件類型,你就可以提供一個(gè)InputStream來(lái)取它的數(shù)據(jù)。從另一方面說(shuō),你可能需要對(duì)這些選項(xiàng)進(jìn)行不同的處理,這個(gè)可以根據(jù)對(duì)這個(gè)選項(xiàng)是不是一個(gè)規(guī)則的表單項(xiàng)來(lái)判斷,即可以根據(jù)這個(gè)數(shù)據(jù)是否來(lái)源于普通的文本框或者一個(gè)簡(jiǎn)單的html表單域,還是一個(gè)能被上傳的文件。FileItem接口提供了多種方法來(lái)判斷它是否是一個(gè)可上傳的文件,然后你就可以用最合適的方式來(lái)處理這些數(shù)據(jù)了。FileUpload使用FileItemFactory來(lái)創(chuàng)建新的文件項(xiàng)目。這正是給FileUpload帶來(lái)靈活性的原因。這個(gè)工廠最終控制每個(gè)項(xiàng)目的創(chuàng)建。默認(rèn)的工廠在內(nèi)存或者磁盤上保存了項(xiàng)目的數(shù)據(jù),這個(gè)可以根據(jù)項(xiàng)目的大小來(lái)定(例如,字節(jié)數(shù)據(jù))。當(dāng)然,這個(gè)動(dòng)作可以通過(guò)自定義來(lái)滿足你應(yīng)用的需要。

          解析請(qǐng)求

          在你對(duì)要上傳的選項(xiàng)處理之前,很顯然的你得先解析這些請(qǐng)求本身。很直接的就是要確保這個(gè)請(qǐng)求是不是一個(gè)要上傳的文件,然而FileUpload使這一點(diǎn)簡(jiǎn)單化了,你只需提供一個(gè)靜態(tài)的方法來(lái)做到這一點(diǎn)。

          //檢查是否是一個(gè)文件上傳請(qǐng)求
          boolean isMultipart = FileUpload.isMultipartContent(request);


          現(xiàn)在我們就可以準(zhǔn)備解析這個(gè)請(qǐng)求到一個(gè)備選的選項(xiàng)了。解析的結(jié)果是一個(gè)文件選項(xiàng)的List,每個(gè)這樣的選項(xiàng)都實(shí)現(xiàn)了FileItem接口,處理這些選項(xiàng)將在下面得到討論。

          最簡(jiǎn)單的情況

          最簡(jiǎn)單的使用場(chǎng)景可以參照下面:

          • 被上傳得選項(xiàng)必須以適度的大小駐留在內(nèi)存中;
          • 比較大的文件上傳選項(xiàng)必須寫入到磁盤的臨時(shí)文件中去;
          • 大文件上傳請(qǐng)求必須不被允許;
          • 默認(rèn)的駐留內(nèi)存的選項(xiàng)的最大大小,最大允許的上傳文件請(qǐng)求,和臨時(shí)文件的存儲(chǔ)地方是可以接受的;

          在這種情景下處理這樣一個(gè)請(qǐng)求并不是很簡(jiǎn)單的:


          //創(chuàng)建一個(gè)新的文件上傳句柄
          DiskFileUpload upload = new DiskFileUpload();
          //解析請(qǐng)求
          List /* FileItem */ items = upload.parseRequest(request);


          這就是所有我們需要做的,真的!
          解析的結(jié)果是一個(gè)文件項(xiàng)目的List,每一個(gè)都實(shí)現(xiàn)了FileItem接口。處理這些項(xiàng)目將在下面討論。

          練習(xí)更多的控制

          如果你的使用情景非常接近最簡(jiǎn)單的使用方式,在上文中可以看到,但是你需要更多的控制臨界的大小和臨時(shí)文件的駐留地址,你可以使用DiskFileUpload類的方法來(lái)自定義這些動(dòng)作,就像這樣:


          //創(chuàng)建一個(gè)新的文件上傳句柄
          DiskFileUpload upload = new DiskFileUpload();
          //設(shè)置上傳參數(shù)
          upload.setSizeThrehold(最大內(nèi)存大小);
          upload.setSizeMax(最大請(qǐng)求大小);
          upload.setRepositoryPath(臨時(shí)目錄);
          //解析請(qǐng)求
          List /* FileItem */ items = upload.parseRequest(request);


          當(dāng)然,每個(gè)配置方法都是獨(dú)立于其它的,但是如果你想一次就配置它們,你可以使用可選的parseRequest()方法,像這樣:


          // 建立一個(gè)新的文件上傳句柄
          DiskFileUpload upload = new DiskFileUpload();
          // 解析請(qǐng)求
          List /* FileItem */ items = upload.parseRequest(request,
          ??????? 內(nèi)存大小, 允許上傳的最大文件, 臨時(shí)目錄);


          如果你想更多地控制請(qǐng)求的解析,比如把上傳選項(xiàng)存儲(chǔ)到其它地方,例如,存到數(shù)據(jù)庫(kù)中-你可以參照自定義FileUpload。

          處理上傳選項(xiàng)

          一旦解析過(guò)程完畢,你就可以獲得一個(gè)文件選項(xiàng)的List,以便進(jìn)一步處理。在大多數(shù)情況下,你將會(huì)根據(jù)規(guī)則的表單域來(lái)不同地處理文件的上傳。所以你可能以這樣的方式來(lái)處理:


          // 處理上傳的選項(xiàng)
          Iterator iter = items.iterator();
          while (iter.hasNext()) {
          ??? FileItem item = (FileItem) iter.next();

          ??? if (item.isFormField()) {
          ??????? processFormField(item);
          ??? } else {
          ??????? processUploadedFile(item);
          ??? }
          }


          對(duì)于一個(gè)規(guī)則的表單域來(lái)說(shuō),你對(duì)它感興趣的可能就只有它的名字以及它的字符串值。你也會(huì)想到,處理它們是簡(jiǎn)單的:


          //處理一個(gè)規(guī)則表單域
          if (item.isFormField()) {
          ??? String name = item.getFieldName();
          ??? String value = item.getString();
          ??? ...
          }


          而對(duì)于一個(gè)文件的上傳,在你處理它的內(nèi)容之前,可以有好多令你想知道的不同的東西,這里有一個(gè)采用了一些你可能感興趣的方法的例子


          // 處理一個(gè)文件上傳
          if (!item.isFormField()) {
          ??? String fieldName = item.getFieldName();
          ??? String fileName = item.getName();
          ??? String contentType = item.getContentType();
          ??? boolean isInMemory = item.isInMemory();
          ??? long sizeInBytes = item.getSize();
          ??? ...
          }


          對(duì)于這些上傳的文件,你一般不想通過(guò)內(nèi)存來(lái)存取它們,除非它們很小,或者你沒(méi)有其它好的方法,更進(jìn)一步,你想將內(nèi)容當(dāng)作文件流來(lái)處理,或者將整個(gè)文件寫到最終的地址。FileUpload提供了簡(jiǎn)單的方法來(lái)完成這些 操作。


          // 處理一個(gè)文件上傳的情況
          if (writeToFile) {
          ??? File uploadedFile = new File(...);
          ??? item.write(uploadedFile);
          } else {
          ??? InputStream uploadedStream = item.getInputStream();
          ??? ...
          ??? uploadedStream.close();
          }


          注意到,在默認(rèn)的FileUpload的實(shí)現(xiàn)中,write()方法將嘗試把文件改名以將它保存到特定的地點(diǎn),如果數(shù)據(jù)已經(jīng)在臨時(shí)文件中了,如果重命名失敗,實(shí)際的復(fù)制文件就完成了(?),在其它原因看來(lái),或者數(shù)據(jù)已經(jīng)在內(nèi)存中了。如果你的確需要在內(nèi)存中取上傳的數(shù)據(jù),你只需簡(jiǎn)單的調(diào)用get()方法來(lái)把它當(dāng)作一個(gè)字符數(shù)組來(lái)獲得。



          // 在內(nèi)存中處理一個(gè)上傳的文件
          byte[] data = item.get();
          ...



          和殺毒軟件的相互作用

          當(dāng)web容器在運(yùn)行時(shí),而殺毒軟件又同時(shí)運(yùn)行在同樣的系統(tǒng)上,這種情況下在應(yīng)用中使用FileUpload容易導(dǎo)致一些很難預(yù)料的事情.這部分將描述一些你可能要遇到的情況,我們會(huì)提供一些方法來(lái)處理它們。默認(rèn)的FileUpload實(shí)現(xiàn)將會(huì)使超過(guò)它在內(nèi)存中大小的上傳的選項(xiàng)寫入到磁盤。而當(dāng)這樣的文件關(guān)閉后,任何系統(tǒng)中的殺毒軟件都會(huì)被喚醒,然后去檢查它,然后會(huì)潛在地隔離這個(gè)文件--就是說(shuō),將它移動(dòng)到一個(gè)不產(chǎn)生問(wèn)題的特定地方。這樣一來(lái),對(duì)開(kāi)發(fā)者來(lái)說(shuō)當(dāng)然是一個(gè)意外,因?yàn)閯倓偵蟼鞯奈募⒉荒鼙惶幚砹恕牧硪环矫鎭?lái)說(shuō),小于設(shè)定的內(nèi)存大小的那些上傳的文件將被保持在內(nèi)存中,這樣一來(lái),將不會(huì)被殺毒軟件所檢測(cè)到,這樣就有可能使病毒以某種方式駐留在了系統(tǒng)中了(雖然如果一旦它被寫入到磁盤,殺毒軟件就會(huì)定位并檢測(cè)到它)。一個(gè)通用的解決方法是在系統(tǒng)中專門設(shè)置一個(gè)目錄來(lái)存放這些上傳的文件,然后配置殺毒軟件忽略這個(gè)目錄。這樣將確保上傳的文件在系統(tǒng)中不被隔離,但是這樣就把掃描病毒的責(zé)任交給了應(yīng)用程序的開(kāi)發(fā)者了。掃描這些上傳的文件的任務(wù)可以在外部的處理中實(shí)現(xiàn)。這樣可以將干凈的文件移動(dòng)到一個(gè)“改進(jìn)”過(guò)的地方,或者也可以把殺毒集成到應(yīng)用中去。至于怎么將外部處理或集成病毒掃描到一個(gè)
          應(yīng)用,這個(gè)已經(jīng)超出了本文檔的討論范圍。

          下一步是什么

          希望這個(gè)頁(yè)面能提供給你一個(gè)好的意見(jiàn),讓你在你自己的應(yīng)用中能使用FileUpload。更多關(guān)于這里介紹的方法,以及其它可用的方法,你可以參照api文檔。這里介紹的用法已經(jīng)可以滿足大多數(shù)的文件上傳的需要了,當(dāng)然,如果你還有更多的復(fù)雜的需求,使用它的靈活的自定義配置的能力,F(xiàn)ileUpload一定可以能夠幫助你。

          另一個(gè)實(shí)例:


          commons.fileupload實(shí)現(xiàn)文件的上傳,代碼如下:
          <%!
          ?//服務(wù)器端保存上傳文件的路徑
          ??? String saveDirectory = "g:\\upload\\";
          ??? // 臨時(shí)路徑 一旦文件大小超過(guò)getSizeThreshold()的值時(shí)數(shù)據(jù)存放在硬盤的目錄
          ??? String tmpDirectory = "g:\\upload\\tmp\\";
          ??? // 最多只允許在內(nèi)存中存儲(chǔ)的數(shù)據(jù)大小,單位:字節(jié)
          ??? int maxPostSize = 1024 * 1024;
          %>
          <%
          ??? // 文件內(nèi)容?
          ??? String FileDescription = null;
          ??? // 文件名(包括路徑)
          ??? String FileName = null;
          ??? // 文件大小
          ??? long FileSize = 0;
          ??? // 文件類型
          ??? String ContentType = null;

          %>

          <%
          ?? DiskFileUpload fu = new DiskFileUpload();
          ??? // 設(shè)置允許用戶上傳文件大小,單位:字節(jié)
          ?? fu.setSizeMax(200*1024*1024);
          ??? // 設(shè)置最多只允許在內(nèi)存中存儲(chǔ)的數(shù)據(jù),單位:字節(jié)
          ?? fu.setSizeThreshold(maxPostSize);
          ??? // 設(shè)置一旦文件大小超過(guò)getSizeThreshold()的值時(shí)數(shù)據(jù)存放在硬盤的目錄
          ?? fu.setRepositoryPath("g:\\upload\\tmp\\");
          ??? //開(kāi)始讀取上傳信息 得到所有文件
          ?? try{
          ????? List fileItems = fu.parseRequest(request);
          ???? }catch(FileUploadException e){
          ???????? //這里異常產(chǎn)生的原因可能是用戶上傳文件超過(guò)限制、不明類型的文件等
          ???????? //自己處理的代碼
          ???? }
          %>
          <%
          ?? // 依次處理每個(gè)上傳的文件
          ?? Iterator iter = fileItems.iterator();
          ?? while (iter.hasNext()) {
          ???? FileItem item = (FileItem) iter.next();
          ?????? //忽略其他不是文件域的所有表單信息
          ???? if (!item.isFormField()) {
          ?????? String name = item.getName();
          ?????? long size = item.getSize();
          ?????? String? contentType = item.getContentType();
          ???? if((name==null||name.equals("")) && size==0)
          ?????? continue;
          %>
          <%
          ?? //保存上傳的文件到指定的目錄
          ? String[] names=StringUtils.split(name,"\\");? //對(duì)原來(lái)帶路徑的文件名進(jìn)行分割
          ?? name = names[names.length-1];
          ?? item.write(new File(saveDirectory+ name));
          ? }
          }
          %>
          ?下面是其簡(jiǎn)單的使用場(chǎng)景:
          ?A、上傳項(xiàng)目只要足夠小,就應(yīng)該保留在內(nèi)存里。
          ?B、較大的項(xiàng)目應(yīng)該被寫在硬盤的臨時(shí)文件上。
          ?C、非常大的上傳請(qǐng)求應(yīng)該避免。
          ?D、限制項(xiàng)目在內(nèi)存中所占的空間,限制最大的上傳請(qǐng)求,并且設(shè)定臨時(shí)文件的位置。
          ?
          ?可以根據(jù)具體使用用servlet來(lái)重寫,具體參數(shù)配置可以統(tǒng)一放置到一配置文件
          ?



          ?文件的下載用servlet實(shí)現(xiàn)
          ????? public void doGet(HttpServletRequest request,
          ?????????????????????? HttpServletResponse response)
          ???? {
          ??????? ?String aFilePath = null;??? //要下載的文件路徑
          ???????? String aFileName = null;??? //要下載的文件名
          ???????? FileInputStream in = null;? //輸入流
          ???????? ServletOutputStream out = null;? //輸出流

          ???????? try
          ???{
          ???????? ?
          ???????????? aFilePath = getFilePath(request);
          ???????????? aFileName = getFileName(request);

          ???????????? response.setContentType(getContentType(aFileName) + "; charset=UTF-8");
          ???????????? response.setHeader("Content-disposition", "attachment; filename=" + aFileName);

          ???????????? in = new? FileInputStream(aFilePath + aFileName); //讀入文件
          ??????????? out = response.getOutputStream();
          ??????????? out.flush();
          ?????????? ?int aRead = 0;
          ?????????? while((aRead = in.read()) != -1 & in != null)
          ?????? ?{
          ???????????? out.write(aRead);
          ???????? }
          ?????????? out.flush();
          ????? }
          ?????? catch(Throwable e)
          ???? {
          ???? log.error("FileDownload doGet() IO error!",e);
          ????? }
          ???????? finally
          ???????? {
          ???????????? try
          ???????????? {
          ???????????????? in.close();
          ???????????????? out.close();
          ???????????? }
          ???????????? catch(Throwable e)
          ???????????? {
          ???????????? ?log.error("FileDownload doGet() IO close error!",e);
          ???????????? }
          ???????? }
          ???? }




          posted on 2006-11-03 20:51 昨夜人生 閱讀(650) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 思茅市| 自治县| 淄博市| 仪陇县| 萝北县| 色达县| 米易县| 汉沽区| 仙居县| 博兴县| 永川市| 彭州市| 云安县| 鄂州市| 岳阳市| 莱西市| 凤城市| 闵行区| 通道| 广宁县| 东辽县| 台东市| 沧源| 沂水县| 高平市| 西乡县| 福建省| 沙湾县| 贺州市| 塔城市| 长沙县| 苗栗市| 大石桥市| 沙田区| 鄂尔多斯市| 日喀则市| 辉南县| 克东县| 自治县| 信阳市| 保山市|