close

 

原文網頁http://commons.apache.org/fileupload/using.html


使用 FileUpload

FileUpload取決於你的程式要求,可以用在許多不同的地方。在最簡單的情況下,你可以用一個method來解析servlet request,然後將清單中的項目使用在程式裡。另一方面,你可以制定每個檔案上傳時所使用的方式,例如:你可以決定檔案上傳至資料庫所用的串流(stream )類型。

在這裡,我們將說明FileUpload的基本運作模式,並說明了一些比較簡單和最常見的的使用範例。自定義的FileUpload 說明

FileUpload Commons IO為基礎,所以一定要確保你有相關頁面中的版本,並且存在你的classpath中。


 

它是如何運作的:

一個文件上傳request 中含有items 的清單,其中內容是以RFC 1867 來編碼的 "Form-based File Upload in HTML"FileUpload可以解析這類request,並提供items 的清單給你的程式。每一個item都實現(implements) FileItemInterface,不論其底層的實現。

本文章將教導讀者來使用傳統的commons FileUpload API。傳統的API是一個方便的方法,然而這裡也提供速度更快的串流 API 

每個item 檔案都帶有屬性,而你的程式可能會需要它們。例如,每一個item 都有name type 來提供InputStream 處理。另外,你可能會對不同的item 做不同的處理,例如上傳的圖片和圖片說明文字可能要存在不同的地方。FileItem interface 提供了方法來處理上述的例子,並以最佳的方式處理資料。

FileUpload FileItemFactory來新建檔案,這使得檔案上傳更有靈活性。FileItemFactory 對每個item 做最後的控制。FileItemFactory 能夠以資料大小來決定資料要存進記憶體還是硬碟。然而,這種規則可以自由訂製,以滿足程式上的需求。


 

ServletPortlet

FileUpload 從版本1.1開始,支持文件在servletportlet的環境上傳。這兩個環境下的用法幾乎是相同的,因此本文章接下來的環境都以servlet來做說明。

如果你要建立一個portlet 應用程式,請注意下面兩點與servlet不同的地方:

  • 當引用ServletFileUpload類的時候,請以PortletFileUpload類來替代。
  • 當引用HttpServletRequest類的時候,請以ActionRequest類來替代。

   


 

解析request

在處理上傳的items之前,你當然必須先解析request。你很容易可以知道request 的內容是不是要上傳檔案,但FileUpload 可以讓它變得更加簡單,它提供了一個靜態方法:


//Check that we have a file upload request

boolean isMultipart = ServletFileUpload.isMultipartContent(request);

得知這是一個檔案上傳request 之後,我們就可以繼續解析其內容items

 

譯者補充:其實判斷句判斷的依據是

<form enctype="multipart/form-data" method="post" action="Sverlet">

中的enctype內容

 最簡單的例子

最簡單的用法如下:

  • 上傳的items如果是相對較小的,則應該保留在記憶體中。
  • 較大的items應寫入到硬碟上的暫存檔案。
  • 不應該允許超大檔案的上傳請求。
  • 記憶體預設的最大使用空間、檔案的大小上限、暫存檔案的目錄都是可以被存取的。

   

最簡單的處理request 的情況:


//Create a factory for disk-based file items

FileItemFactory factory = new DiskFileItemFactory();
//Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
//Parse the request
List /* FileItem */ items = upload.parseRequest(request);

只需要以上這些動作就完成了。真的!

分析request可以得到一個List,裡面是實作了FileItem 介面的items處理這些items的方法將在下面討論。

做更多的控制

即使你所需要的操作很接近上述的簡單例子,你還是需要多一點的控制,像是上傳的設定或是FileItemFactory 中的設定。以下是一些配置的範例:


//Create a factory for disk-based file items

DiskFileItemFactory factory = new DiskFileItemFactory();

//Set factory constraints
factory.setSizeThreshold(yourMaxMemorySize);

factory.setRepository(yourTempDirectory);

//Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);

//Set overall request size constraint
upload.setSizeMax(yourMaxRequestSize);


//Parse the request
List /* FileItem */ items = upload.parseRequest(request);

當然,每個配置方法是彼此獨立,但如果你想做整體的配置,你可以使用建構子來實現,像這樣:


//Create a factory for disk-based file items

DiskFileItemFactory factory = new DiskFileItemFactory(
yourMaxMemorySize, yourTempDirectory);

如果您需要更進一步控制,例如將資料存入資料庫,請參閱自訂上傳


 

處理上傳的items

一旦request的解析完成後,你會得到一個List,裡面就是你要處理的items。在大多數情況下,你可能會需要對不同檔案的來源做不同的處理,像這樣做:


//Process the uploaded items
Iterator iter = items.iterator();
while (iter.hasNext()) {
    FileItem item = (FileItem) iter.next();

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

對於普通的表格,你通常只會對它的name 和它的String值做處理。


//Process a regular form field
if (item.isFormField()) {
    String name = item.getFieldName();
    String value = item.getString();
    ...
}

對於檔案上傳,在處理內容之前,你可能會想先知道一些關於檔案的訊息。下面是一些比較常見的的例子


//Process a file upload

if (!item.isFormField()) {
    String fieldName = item.getFieldName();
    String fileName = item.getName();
    String contentType = item.getContentType();
    boolean isInMemory = item.isInMemory();
    long sizeInBytes = item.getSize();
    ...
}

上傳文件的時候,你通常不會經由記憶體來處理,除非檔案很小,或者你有沒有其他選擇。相反的,直接用串流的方式來處理檔案,把整個檔案直接寫到最後的位置是最理想的。FileUpload 可以很容易的實現這兩種方法。


//Process a file upload

if (writeToFile) {
    File uploadedFile = new File(...);
    item.write(uploadedFile);
} else {
    InputStream uploadedStream = item.getInputStream();
    ...
    uploadedStream.close();
}

請注意,預設上,FileUpload write() 方法會在資料已經完全傳到暫存檔中之後嘗試重命名該檔案。即使重新命名失敗,複製檔案的動作也會告終,重新命名失敗可能出於某種原因,例如檔案是存在於記憶體中。

如果你要存取位於記憶體中的檔案,你可以使用get()方法來取得一個bytes矩陣的資料。


//Process a file upload in memory

byte[] data = item.get();
...

 


資源清理

本節僅適用於DiskFileItem 的使用者。如果您上傳的檔案在被寫入到暫存檔之前就要做處理,那麼閱讀這節會有幫助。

如果暫存檔不再被使用時,就會被自動刪除,(更確切地說,在java.io.File中有garbage collecter,而在這裏是org.apache.commons.io.FileCleaner會有個執行序來做資源回收的動作。

資源回收執行序不再需要時,應該被停止。在servlet環境中,會有一個特殊的servlet上下文listener來做這樣的事情,稱為 FileCleanerCleanup 。要做到這一點,在web.xml 中加入以下標籤:


<web-app>
    ...
    <listener>
        <listener-class>
            org.apache.commons.fileupload.servlet.FileCleanerCleanup
        </listener-class>
    </listener>
    ...
</web-app>

建立一個 DiskFileItemFactory

FileCleanerCleanup提供了一個實例org.apache.commons.io.FileCleaningTracker. 當建立org.apache.commons.fileupload.disk.DiskFileItemFactory 時,FileCleaningTracker實例必須被使用。請參考下面的做法:


    public static DiskFileItemFactory newDiskFileItemFactory(
            ServletContext context, File repository) {
        FileCleaningTracker fileCleaningTracker = FileCleanerCleanup
                .getFileCleaningTracker(context);
        DiskFileItemFactory factory = new DiskFileItemFactory(
                DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD, repository);
        factory.setFileCleaningTracker(fileCleaningTracker);
        return factory;
    }

不讓暫存檔被清理

FileCleaningTracker 設為null 以停止追蹤暫存檔。這樣一來創建的檔案將不再被追蹤,也不再被自動刪除。


 

與防毒軟體的互動

在使用FileUpload 時,同時在一個web container底下運作多個防毒程序可能會有意料之外的結果。本節介紹您可能會遇到的一些行為,並提供了一些處理它們方法。

使用FileUpload 來傳送檔案到磁碟中,當傳送結束,檔案被關閉之後,系統上的病毒掃描程序將會開始檢查該檔案。如果該檔案被查出有問題,就會被防毒軟體移動到隔離所或刪除。這樣一來,我們的檔案處理程序就會出現意外,因為檔案已經不存在。另一方面,上傳的項目仍然被保存在記憶體中,因此不會被掃毒軟體發現。這可能會讓病毒有機會留下來(雖然最後檔案還是會進到硬碟被掃毒軟體檢查)。

一個常用的解決方案是把所有上傳的檔案放在一個特定的目錄中,並設定防毒軟體忽略該目錄。這能夠確保檔案不會發生像上述一樣的事情。詳細的掃毒不在本文詳述範圍。


 

進度條

如果會遇到大檔案的上傳,那進度條會是一個很好的表現方法來告知使用者上傳的情況。即使是HTML頁面,也可以實現進度條,通過回傳 multipart/replace 之類的response

使用進度條會用到一個listener


//Create a progress listener

ProgressListener progressListener = new ProgressListener() {
    public void update(long pBytesRead, long pContentLength,
            int pItems) {
        System.out.println("We are currently reading item "
                + pItems);

        if (pContentLength == -1) {
            System.out.println("So far, " + pBytesRead
                    + " bytes have been read.");
        } else {
            System.out.println("So far, " + pBytesRead + " of "
                    + pContentLength + " bytes have been read.");
        }
    }    };
upload.setProgressListener(progressListener);

你可以試試看把上面這個listener 用在你的進度條上,你會發現有個問題存在:listener 被呼叫的頻率非常高因為它可能會被所有網路封包給觸發!換句話說,你的進度條會造成電腦的負擔!這裡提供一個典型的解決方案,減少進度監聽活動。例如,您可以只監聽Mb為單位的變動:


//Create a progress listener

ProgressListener progressListener = new ProgressListener() {
    private long megaBytes = -1;

        public void update(long pBytesRead, long pContentLength,
            int pItems) {
        long mBytes = pBytesRead / 1000000;
        if (megaBytes == mBytes) {
            return;
        }
        megaBytes = mBytes;
        System.out.println("We are currently reading item "
                + pItems);

        if (pContentLength == -1) {
            System.out.println("So far, " + pBytesRead
                    + " bytes have been read.");
        } else {
            System.out.println("So far, " + pBytesRead + " of "
                    + pContentLength + " bytes have been read.");
        }
    }
};

 


接下來?

希望這篇文章對你有幫助對於如何上傳檔案。方法詳細介紹以及其他可用的方法,請參考JavaDoc

這裡所描述的用法應滿足大部分的檔案上傳需要。如果你有更進階的需要,FileUpload也可以提供更多的功能來使用

arrow
arrow
    全站熱搜

    yaya741228 發表在 痞客邦 留言(0) 人氣()