上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

役に立ったらぽちっとよろしく。 人気ブログランキングへ

JSFの現在のバージョン1.2ではファイルのアップロード処理は標準では提供されていないっぽい。で、ネットで検索したらいくつか対策は見つかるけど、ちょっと自分には合わない…残念。…ので、我流の方法を試して見る事に。

ネットで見つかったのはこんなの

  • Apach MyFacesを使えばファイルアップロード用のタグがあるらしいのでそれを使う。
  • ファイルのアップロード部分だけServlet+JSP方式で処理する。

問題と思うのは…。最初の方法の場合、MyFacesはファイルアップロードのためだけのライブラリではないので、ファイルのアップロードのためだけに巨大なライブラリを入れたくない場合に不向き。次の方法の場合、必要なライブラリはApach FileUpload等だけで済むけど、同じページのファイルアップロード以外の部分をServletでガリガリ処理する事になる。

我流の方法ってのはこんなの

  1. Servlet Filterでリクエストを捕まえる。
  2. リクエストを解析し(HTTP&POST&multipart/form-data)以外は普段通りの処理をする。
  3. 目的のリクエストの場合はJSF用のServletが処理出来るよう、HttpServletRequestWrapperで 解析したリクエストをapplication/x-www-form-urlencoded形式に変換。ファイル形式のパラメタは リクエストスコープの属性として格納。その後は普段どおりに処理。

こうすればJSF用のServletがmultipart/form-data形式に対応出来るし、必要なライブラリもApache FileUploadだけで済む。別の方法で、自前でリクエストの解析からJSFのライフサイクル実行まで行うってのも考えられるけど…リクエストをラップするこの方法の方がコードは断然短いはず。

ちなみに…有名なサンプルプログラム PetStore ではファイルのアップロードはAJAXを使ってるっぽい。たぶんAJAXによるファイルのアップロードリクエストをShale RemotingがApache FileUploadで解析する部品に流してる雰囲気。でどうも詳しく見ていくとAJAX経由での処理の流れしか想定してないみたい。てかそもそもPetStoreはJavaScriptをOFFにするとページが表示されなくなったりする。

というかJSF、ファイルアップロード位は標準で備えてて欲しいような…。
けど、ま、いっか!いい方法思いついたし。今さら標準なんて…!!
…うけけ!おいらは自分が良ければそれでいいって性格なのさ!!

役に立ったらぽちっとよろしく。 人気ブログランキングへ


不完全だけどフィルタのコードはだいたいこんな感じ。。。

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

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

import com.sun.org.apache.commons.collections.IteratorUtils;

import vmapWeb.AppRequest;
import vmapWeb.FileParameter;

public class FileUploadFilter implements Filter {

	private static int MAX_REQUEST_SIZE = 500 * 1024;
	
    @Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		chain.doFilter(getWrappedRequest(request), response);
	}

	@Override
	public void init(FilterConfig fConfig) throws ServletException {
	}

	// リクエストを状況に応じてラップします。
	private ServletRequest getWrappedRequest(ServletRequest request) throws ServletException{
		try {
			if(!(request instanceof HttpServletRequest))
				return request;
			HttpServletRequest httpRequest = (HttpServletRequest)request;
			if(!httpRequest.getMethod().equals("POST"))
				return request;
			else if(!httpRequest.getContentType().startsWith("multipart/form-data"))
				return request;
			return new FileUploadRequest(httpRequest);
		} catch (FileUploadException e) {
			throw new ServletException(e);
		} catch (IOException e) {
			throw new ServletException(e);
		}
	}
	
	// multipart用に本来のリクエストをラップしたリクエスト。
	private class FileUploadRequest extends HttpServletRequestWrapper {

		// ファイルパラメタ以外の通常パラメタ
		private Map<String, String[]> normals;
		
		// 通常のリクエストをラップ氏構築します。
		public FileUploadRequest(HttpServletRequest request) throws FileUploadException, IOException {
			super(request);
			// 通常パラメタの詰め込み先を生成
			Map<String, List<String>> parsingParams = new HashMap<String, List<String>>();
			// アップロードファイルをディスクに退避するオブジェクトを生成
			DiskFileItemFactory factory = new DiskFileItemFactory();
			factory.setSizeThreshold(1024);
			// アップロードを処理するオブジェクトの用意
			ServletFileUpload upload = new ServletFileUpload(factory);
			upload.setSizeMax(MAX_REQUEST_SIZE);
			upload.setHeaderEncoding("utf-8");
			// パラメタを順に処理
			Iterator<?> paramIterator = upload.parseRequest(request).iterator();
			while (paramIterator.hasNext()) {
				FileItem fItem = (FileItem) paramIterator.next();
				if (fItem.isFormField()) {
					if(!parsingParams.containsKey(fItem.getFieldName()))
						parsingParams.put(fItem.getFieldName(), new LinkedList<String>());
					parsingParams.get(fItem.getFieldName()).add(fItem.getString());
				} else {
					String name = fItem.getFieldName();
					FileParameter fileParam = new FileParameter(
							fItem.getInputStream(),fItem.getSize());
					AppRequest.getRequest(request).setFileParameter(name, fileParam);
				}
			}
			normals = parseDone(parsingParams);
		}

		@Override
		public String getContentType() {
			return "application/x-www-form-urlencoded";
		}

		@Override
		public String getParameter(String name) {
			return getParameterValues(name)!=null?getParameterValues(name)[0]:null;
		}

		@Override
		public Map<?,?> getParameterMap() {
			return normals;
		}

		@Override
		public Enumeration<?> getParameterNames() {
			return IteratorUtils.asEnumeration(normals.keySet().iterator());
		}

		@Override
		public String[] getParameterValues(String name) {
			return normals.get(name);
		}
		
		private Map<String, String[]> parseDone(Map<String, List<String>> parsingParams) {
			Map<String, String[]> retMap = new HashMap<String, String[]>();
			for(Map.Entry<String, List<String>> entry:parsingParams.entrySet()) {
				if(entry.getValue().size() == 0)
					retMap.put(entry.getKey(), null);
				retMap.put(entry.getKey(), entry.getValue().toArray(new String[]{}));
			}
			return Collections.unmodifiableMap(retMap);
		}
		
	}
	
}
スポンサーサイト
コメント
この記事へのコメント
このコメントは管理人のみ閲覧できます
2010/04/07(水) 18:19:10
コメントを投稿する

トラックバック
この記事のトラックバックURL
この記事へのトラックバック
Copyright © ふらふら技術者の日記 All Rights Reserved.
Powered by FC2 Blog
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。