自寫自看

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

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

          它怎么工作

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

          解析請求

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

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


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

          最簡單的情況

          最簡單的使用場景可以參照下面:

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

          在這種情景下處理這樣一個請求并不是很簡單的:


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


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

          練習更多的控制

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


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


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


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


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

          處理上傳選項

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


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

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


          對于一個規(guī)則的表單域來說,你對它感興趣的可能就只有它的名字以及它的字符串值。你也會想到,處理它們是簡單的:


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


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


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


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


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


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



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



          和殺毒軟件的相互作用

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

          下一步是什么

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

          另一個實例:


          commons.fileupload實現(xiàn)文件的上傳,代碼如下:
          <%!
          ?//服務(wù)器端保存上傳文件的路徑
          ??? String saveDirectory = "g:\\upload\\";
          ??? // 臨時路徑 一旦文件大小超過getSizeThreshold()的值時數(shù)據(jù)存放在硬盤的目錄
          ??? String tmpDirectory = "g:\\upload\\tmp\\";
          ??? // 最多只允許在內(nèi)存中存儲的數(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)存中存儲的數(shù)據(jù),單位:字節(jié)
          ?? fu.setSizeThreshold(maxPostSize);
          ??? // 設(shè)置一旦文件大小超過getSizeThreshold()的值時數(shù)據(jù)存放在硬盤的目錄
          ?? fu.setRepositoryPath("g:\\upload\\tmp\\");
          ??? //開始讀取上傳信息 得到所有文件
          ?? try{
          ????? List fileItems = fu.parseRequest(request);
          ???? }catch(FileUploadException e){
          ???????? //這里異常產(chǎn)生的原因可能是用戶上傳文件超過限制、不明類型的文件等
          ???????? //自己處理的代碼
          ???? }
          %>
          <%
          ?? // 依次處理每個上傳的文件
          ?? 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,"\\");? //對原來帶路徑的文件名進行分割
          ?? name = names[names.length-1];
          ?? item.write(new File(saveDirectory+ name));
          ? }
          }
          %>
          ?下面是其簡單的使用場景:
          ?A、上傳項目只要足夠小,就應(yīng)該保留在內(nèi)存里。
          ?B、較大的項目應(yīng)該被寫在硬盤的臨時文件上。
          ?C、非常大的上傳請求應(yīng)該避免。
          ?D、限制項目在內(nèi)存中所占的空間,限制最大的上傳請求,并且設(shè)定臨時文件的位置。
          ?
          ?可以根據(jù)具體使用用servlet來重寫,具體參數(shù)配置可以統(tǒng)一放置到一配置文件
          ?



          ?文件的下載用servlet實現(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) 評論(0)  編輯  收藏


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


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 黑河市| 阳高县| 城口县| 嘉义市| 石台县| 汉沽区| 宣汉县| 鄂伦春自治旗| 阿合奇县| 东阿县| 连平县| 大竹县| 保定市| 鄂伦春自治旗| 东台市| 宣城市| 台南县| 分宜县| 交城县| 灵丘县| 阳西县| 巴彦县| 阿鲁科尔沁旗| 涟水县| 涿州市| 海口市| 凤城市| 盐边县| 沈阳市| 柳江县| 定州市| 乳山市| 会东县| 甘孜| 陈巴尔虎旗| 平阴县| 平果县| 隆回县| 遵义县| 全州县| 邓州市|