Java Servlet の ファイルアップロード処理でストリーミングを使う方法

Java Servlet でファイルアップロード処理を実装する場合、
（というか multipart の受信処理を実装する場合、と書いたほうが適切かも）
Apache Commons の [ServletFileUpload](http://commons.apache.org/proper/commons-fileupload/apidocs/org/apache/commons/fileupload/servlet/ServletFileUpload.html) クラスを使用するのが定石ですが、
大容量ファイルのアップロードで __parseRequest__ メソッドを使うと
__parseRequest__ がなかなか復帰しません。

例えば、以下の様なコードの場合、

```java

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

protected void parse(HttpServletRequest request) throws Exception {
	DiskFileItemFactory factory = new DiskFileItemFactory();
	ServletFileUpload upload = new ServletFileUpload(factory);

	factory.setSizeThreshold(1024);
	upload.setSizeMax(-1);
	upload.setHeaderEncoding("UTF-8");

	List list = upload.parseRequest(request);
	Iterator iterator = list.iterator();

	while (iterator.hasNext()) {
		FileItem fileitem = (FileItem)iterator.next();
		if (fileitem.isFormField()) {
			trace("key=" + fileitem.getFieldName());
			trace("value=" +  fileitem.getString());
			
		} else {
			trace("key=" + fileitem.getFieldName());
			
			InputStream is = fileitem.getInputStream();
			// InputStream を使ってメモリに展開するなり、
			// ファイルに書き込むなり・・・
			int len = 0;
			byte[] buffer = new byte[1024];
			
			FileOutputStream fos = new FileOutputStream("foo.dat");
			
			try {
				while((len = is.read(buffer)) > 0) {
					fos.write(buffer, 0, len);
				}
			} finally {
				fos.close();
			}
		}
	}
}
```
一見良さそうですが、実は __parseRequest__ メソッドは全てのデータ受信が終わるまで復帰しません。
ということは、例えば、大容量のデータをファイルに書き込む場合は、
全てのデータ受信が終わってから、まとめてファイルに書き込むことになります。

数キロバイト程度のデータであれば別にこれでも良いですが、数100MBからGB単位になると
ファイル書き込みに時間がかかり、ちょっと困ります。

実は、全てのデータ受信が終わってからパースするのではなく、逐次処理する方法が存在します。

```java
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;

protected void parse(HttpServletRequest request) throws Exception {
	DiskFileItemFactory factory = new DiskFileItemFactory();
	ServletFileUpload upload = new ServletFileUpload(factory);

	factory.setSizeThreshold(1024);
	upload.setSizeMax(-1);
	upload.setHeaderEncoding("UTF-8");
	
	FileItemIterator iter = upload.getItemIterator(request);
	while (iter.hasNext()) {
		FileItemStream item = iter.next();

		if (item.isFormField()) {
			InputStream is = item.openStream();

			trace("key=" + item.getFieldName());
			trace("value=" +  Streams.asString(is));
		} else {
			trace("key=" + item.getFieldName());
			InputStream is = item.openStream();

			// InputStream を使ってメモリに展開するなり、
			// ファイルに書き込むなり・・・
			int len = 0;
			byte[] buffer = new byte[1024];
			
			FileOutputStream fos = new FileOutputStream("foo.dat");
			
			try {
				while((len = is.read(buffer)) > 0) {
					fos.write(buffer, 0, len);
				}
			} finally {
				fos.close();
			}
		}
	}
}
```
__parseRequest__ メソッドの代わりに __getItemIterator__ メソッドを使います。
※ [Streaming](http://commons.apache.org/proper/commons-fileupload/streaming.html) と呼んでいるようです

__getItemIterator__ メソッドは即復帰し、その後は __getItemIterator__ メソッドで返された
イテレータで逐次処理することができます。

イテレータの __next__ メソッドでは __FileItemStream__ クラスのインスタンスが返ってきます。
__FileItemStream__ の __openStream__ メソッドでストリームをオープンした後は、
ファイルに書くなりなんらかの解析をさせるなりといったことが可能です。

この方法だと、データ受信と並行して処理をさせることができるので、待ち時間の短縮になって良いです。
（あと試してないですがおそらくヒープメモリの節約になりそうな気がします。
 __parseRequest__ は全データを保持する必要があるので・・・）