参考にさせていただいたサイト
The BalusC Code: Uploading files with JSF 2.0 and Servlet 3.0
http://balusc.blogspot.com/2009/12/uploading-files-with-jsf-20-and-servlet.html
CommonsのFileUploadで扱うわけだけど、
さらにSpringのMultipartFileにしてManagedBeanのプロパティにセットするようにした。
まずFilter。当然、web.xmlで定義してね。
import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; 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; public class FileUploadFilter implements Filter { private String tmpDirectoryPath; private String encoding; @Override public void init(FilterConfig filterConfig) throws ServletException { this.tmpDirectoryPath = System.getProperty("java.io.tmpdir"); String enc = filterConfig.getInitParameter("encoding"); this.encoding = enc == null || "".equals(enc) ? System .getProperty("file.encoding") : enc; } @Override @SuppressWarnings("unchecked") public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!(request instanceof HttpServletRequest)) { chain.doFilter(request, response); return; } HttpServletRequest httpRequest = (HttpServletRequest) request; if (!ServletFileUpload.isMultipartContent(httpRequest)) { chain.doFilter(request, response); return; } DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setRepository(new File(this.tmpDirectoryPath)); ServletFileUpload upload = new ServletFileUpload(factory); Map map = new HashMap(); List items = null; try { items = upload.parseRequest(httpRequest); } catch (FileUploadException e) { ServletException se = new ServletException(); se.initCause(e); throw se; } for (FileItem fileItem : items) { if (fileItem.isFormField()) { processFormField(fileItem, map); } else { processFileField(fileItem, httpRequest); } } request = wrapRequestToExcludeFileFromParamters(httpRequest, map); chain.doFilter(request, response); } private void processFileField(FileItem fileItem, HttpServletRequest request) { request.setAttribute(fileItem.getFieldName(), fileItem); } private void processFormField(FileItem fileItem, Map map) throws ServletException { String name = fileItem.getFieldName(); String value; try { value = fileItem.getString(this.encoding); } catch (UnsupportedEncodingException e) { throw new ServletException(e); } String[] values = map.get(name); if (values == null) { map.put(name, new String[] { value }); return; } int l = values.length; String[] newValues = new String[l + 1]; System.arraycopy(values, 0, newValues, 0, l); newValues[l] = value; map.put(name, newValues); } private HttpServletRequest wrapRequestToExcludeFileFromParamters( HttpServletRequest request, final Map parameterMap) { // パラメータにアップロードされたファイルを含めないように, リクエストオブジェクトをラップする. return new HttpServletRequestWrapper(request) { @Override public Map getParameterMap() { return parameterMap; } @Override public String[] getParameterValues(String name) { return parameterMap.get(name); } @Override public String getParameter(String name) { String[] parameters = getParameterValues(name); return parameters == null ? null : parameters[0]; } @Override public Enumeration getParameterNames() { return Collections.enumeration(parameterMap.keySet()); } }; } @Override public void destroy() { // nop } }
import java.util.Collection; import javax.el.ValueExpression; import javax.faces.component.EditableValueHolder; import javax.faces.component.FacesComponent; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.behavior.ClientBehavior; import javax.faces.component.behavior.ClientBehaviorHolder; import javax.faces.context.FacesContext; import javax.servlet.http.HttpServletRequest; import FileUploadRenderer; import org.apache.commons.fileupload.FileItem; import org.springframework.web.multipart.commons.CommonsMultipartFile; @FacesComponent("fileUpload") public class HtmlFileUpload extends UIInput implements ClientBehaviorHolder { private static final java.util.Collection CLIENT_EVENTS_LIST = java.util.Collections .unmodifiableCollection(java.util.Arrays.asList("valueChange")); protected enum PropertyKeys { onchange, size } @Override public String getRendererType() { return FileUploadRenderer.class.getName(); } @Override public String getFamily() { return "FileUpload"; } @Override public Collection getEventNames() { return CLIENT_EVENTS_LIST; } @Override public String getDefaultEventName() { return ""; } @Override public void addClientBehavior(String eventName, ClientBehavior behavior) { // no-op } @Override public void decode(FacesContext context) { HttpServletRequest request = (HttpServletRequest) context .getExternalContext().getRequest(); String clientId = getClientId(context); /* * FileUploadFilterでリクエストにアップロードファイルをセットしているため、 * エレメントのIDでリクエストから取り出す。 */ FileItem fileItem = (FileItem) request.getAttribute(clientId); ValueExpression expression = getValueExpression("value"); if (expression != null && fileItem != null) { UIComponent component = getCurrentComponent(context); EditableValueHolder holder = (EditableValueHolder) component; /* * Commons FileUploadで処理されたアップロードファイルを * SpringのMultipartFileでラップして、 * アプリケーション側で扱う。 */ holder.setSubmittedValue(new CommonsMultipartFile(fileItem)); holder.setValid(true); } } public String getOnchange() { return (String) getStateHelper().eval(PropertyKeys.onchange); } public void setOnchange(String onchange) { getStateHelper().put(PropertyKeys.onchange, onchange); } public Long getSize() { return (Long) getStateHelper().eval(PropertyKeys.size); } public void setSize(Long size) { getStateHelper().put(PropertyKeys.size, size); } }
JSFコンポーネントのレンダラ。
JSFの設定ファイルか、@FacesRendererで定義する。
import java.io.IOException; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import org.apache.commons.lang.StringUtils; public class FileUploadRenderer extends HtmlRenderer { @Override public void encodeEnd(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); final String name = "input"; writer.startElement(name, null); String clientId = component.getClientId(); writer.writeAttribute("id", clientId, "id"); writer.writeAttribute("name", clientId, "clientId"); writer.writeAttribute("type", "file", "file"); String styleClass = (String) component .getAttributes() .get("styleClass"); if (StringUtils.isNotBlank(styleClass)) { writer.writeAttribute("class", styleClass, "styleClass"); } String style = (String) component.getAttributes().get("style"); if (StringUtils.isNotBlank(style)) { writer.writeAttribute("style", style, "style"); } writer.endElement(name); writer.flush(); } }
JSFの設定ファイルなら、こんな感じで定義する。
<renderer> <component-family>net.kronosjp.jyukutyo.FileUpload</component-family> <renderer-type>net.kronosjp.jyukutyo.core.api.web.render.FileUploadRenderer</renderer-type> <renderer-class>net.kronosjp.jyukutyo.core.api.web.render.FileUploadRenderer</renderer-class> </renderer>
Faceletsの設定ファイルで、タグを定義する。
<tag> <tag-name>fileUpload</tag-name> <component> <component-type>fileUpload</component-type> </component> </tag>
これで完了。ファイルサイズをバリデートする場合、Validatorを作る。
import java.util.Arrays; import java.util.List; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.FacesValidator; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; import org.apache.commons.lang.StringUtils; import org.springframework.web.multipart.MultipartFile; /** * アップロードファイルバリデータ。 * */ @FacesValidator(value = "net.kronosjp.jyukutyo.fileUploadValidator") public class UploadedFileValidator implements Validator { // 200kb private static final Long DEFAULT_MAX_FILE_SIZE = 200000L; public static final String NOT_FOUND_MESSAGE_ID = "net.kronosjp.jyukutyo.fileUploadValidator.NOT_FOUND"; public static final String NOT_MATCHED_MESSAGE_ID = "net.kronosjp.jyukutyo.fileUploadValidator.NOT_MATCHED"; public static final String SIZE_OVER_MESSAGE_ID = "net.kronosjp.jyukutyo.fileUploadValidator.SIZE_OVER"; // 設定最大サイズ private Long size; /** 有効な拡張子 */ private static final List<String> VALID_EXTENSIONS = Arrays.asList(new String[] { "jpg", "jpeg" }); @Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { if (value == null) { return; } final Long maxSize; if (this.size == null || this.size <= 0) { maxSize = DEFAULT_MAX_FILE_SIZE; } else { maxSize = this.size; } MultipartFile file = (MultipartFile) value; if (file.isEmpty() && StringUtils.isNotEmpty(file.getOriginalFilename())) { throw new ValidatorException("ファイルがありません"); } else if (file.isEmpty()) { return; } String extension = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1).toLowerCase(); if (!VALID_EXTENSIONS.contains(extnsion)) { throw new ValidatorException("有効な拡張子ではありません"); } if (maxSize < file.getSize()) { throw new ValidatorException("サイズオーバーです"); } } public Long getSize() { return this.size; } public void setSize(Long size) { this.size = size; } }