Struts1.2.xでファイルアップロード
ちと最近、Struts1.2.xでファイルアップロードやって面倒だったことがあったので、いまさらまとめ。
マルチパートフォーム処理のアクションから別アクションにフォワードさせると、フォワード先ではパラメーターがアクションフォームに設定されない。
でも、getParameterでは取得できる。
ということでgetParameterした値をアクションフォームに設定するアクションをフォワードの間に挟むと解決できる。
org.apache.struts.util.RequestUtils#populate(java.lang.Object, java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
の処理そのまんまです。
public class ForwardFromMultipartAction extends Action { @Override public String doAction(ApRequestContext ctx, ApForm actionForm) { Enumeration<?> names = getHttpServletRequest(ctx).getParameterNames(); HashMap<String, Object> properties = new HashMap<String, Object>(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); Object parameterValue = getHttpServletRequest(ctx) .getParameterValues(name); if (!(name.startsWith("org.apache.struts."))) { properties.put(name, parameterValue); } } try { BeanUtils.populate(actionForm, properties); } catch (Exception e) { throw new ApplicationException("BeanUtils.populate", e); } return FWD_SUCCESS; } }
ファイルアップロードの入れ子(ネスト)
ファイルアップロードの話というより、strutsの入れ子Listのパラメータ設定の話です。
FormFileを要素に持つListをFileListとして、またそのFileListを要素に持つListをFileListListとしてみる。
ということで、アクションフォームはこんな風に作る。
/** 全レコードのアップロードファイルリスト */ private FileListList fileListList = new FileListList(); /** * 全レコードのアップロードファイルリスト を返します. * @return 全レコードのアップロードファイルリスト */ public FileListList getFileListList() { return fileListList; } /** * 全レコードのアップロードファイルリストを設定します. * @param fileListList 全回答のアップロードファイルリスト */ public void setFileListList(FileListList fileListList) { this.fileListList = fileListList; } /** * 全レコードのアップロードファイルを保持するリスト */ public class FileListList extends ArrayList<FileList> { public FileList get(int index) { while (this.size() <= index) { this.add(new FileList()); } return super.get(index); } } /** * 各レコードのアップロードファイルを保持するリスト */ public class FileList extends ArrayList<FormFile> { public void setFormFile(int index, FormFile formFile) { this.add(index, formFile); } }
HTMLがこんな感じで出力されるように、JSPを書く。
1レコード目
<input type="file" value="" name="fileListList[0].formFile[0]"/>
<input type="file" value="" name="fileListList[0].formFile[1]"/>
2レコード目
<input type="file" value="" name="fileListList[1].formFile[0]"/>
<input type="file" value="" name="fileListList[1].formFile[1]"/>
3レコード目
<input type="file" value="" name="fileListList[2].formFile[0]"/>
例として「fileListList[2].formFile[0]」の値がアクションフォームに設定されるロジックを見て理解してみる。
- org.apache.commons.beanutils.PropertyUtilsBean#getIndexedProperty()がfileListList.get(2)を呼び出す。
- 上記で呼び出して取得したインスタンスに対しorg.apache.commons.beanutils.PropertyUtilsBean#setIndexedProperty()が取得したインスタンス.setFormFile(0, FormFileインスタンス)を呼び出す。
つまり
- fileListList.get(2).setFormFile(0, FormFileインスタンス)、つまりFileListListの2番目のFileListのsetFormFileを使って0番目にFormFileをセットする。
- FileListListやFileListがArrayListを継承する理由は以下のメソッドでListの場合の処理を行ってほしいため。
- PropertyUtilsBean#getIndexedProperty()
- PropertyUtilsBean#setIndexedProperty()
- FileListListでget()をオーバーライドしているのはget(int)が確実に値を返す必要があるから。
- FileListでsetFormFile()を作成したのはPropertyUtilsBean#setIndexedProperty()がformFile[n]という文字列からsetFormFile(int, FormFile)を呼び出すからファイル要素のnameに対応させる必要がある。