Servlet APIにファイルアップロードが用意されたのはみんな知っていると思うが

つりくさいタイトルのほうがいいらしいので。

実はJavaOne時に発表されたpreview版では未実装だったのだけれども、今最新版にしてみたらちゃんと動いたので報告。


JavaEE 6、つまりServlet 3.0ではServletの定義にweb.xmlが必要なくなるとかソースで動的にフィルタやサーブレットを設定可能になったりかなり便利になったと思う。
この辺参照 http://d.hatena.ne.jp/shin/20090616/p1


ほかにも大きいものとしてやっと標準でマルチパートに対応した。つまり、サーブレットAPIで簡単にファイルアップロードが出来るのである。


ソースを見てもらうのが手っ取り早いので先に乗せる。

index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Hello World!</h1>
        <form action="upload" method="post" enctype="multipart/form-data">
            <input type="text" name="text"><br>
            <input type="file" name="file">
            <input type="submit" value="送信">
        </form>
    </body>
</html>

NewServlet.java

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@WebServlet("/upload")
public class NewServlet extends HttpServlet {
   
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet NewServlet</title>");  
            out.println("</head>");
            out.println("<body>");
            for(Part p :request.getParts()){
                out.println("------------------------------------<br>");
                for(String h:p.getHeaderNames()){
                    out.println(p.getHeader(h)+",");
                }
                out.println("<br>");
                out.println(p.getContentType()+":"+p.getName() + "="+p.getSize()+"バイト");
                out.println("<br>");
                Reader r = new InputStreamReader( p.getInputStream() ,"UTF-8");
                int ch;
                while((ch = r.read()) >= 0){
                    out.print((char)(ch & 0xffff));
                }
                r.close();
                out.println("<br>");
            }
            out.println("</body>");
            out.println("</html>");
        } finally { 
            out.close();
        }
    } 

}

アノテーションマッピングしているのでweb.xmlは例によってなくてよい。

実行

テキスト入力とマニフェストファイルをアップロードしてみる。


結果


どうなってるかはソースと照らし合わせるとすぐにわかると思う。すごい簡単に扱えるのがわかったでしょうか。

ファイルサイズの制限等は?

MultipartConfigにてリクエストのサイズ制限とかファイルアップロードのサイズ制限とか、サイズがこれ以上になったらメモリで確保ではなく、ファイルに書き出すようにするとか設定が出来る。

@WebServlet("/upload")
@MultipartConfig(maxFileSize=10)
public class NewServlet extends HttpServlet {
…

たとえば、上のようにして先ほどのファイル(25バイト)をアップロードすると

致命的: StandardWrapperValve[NewServlet]: PWC1406: Servlet.service() for servlet NewServlet threw exception
java.lang.IllegalStateException: org.apache.catalina.fileupload.SizeException: The field text exceeds its maximum permitted  size of 10 characters.
        at org.apache.catalina.fileupload.Multipart.initParts(Multipart.java:134)
        at org.apache.catalina.fileupload.Multipart.getParts(Multipart.java:143)
        at org.apache.catalina.connector.Request.getParts(Request.java:4042)
        at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1166)
        at NewServlet.doPost(NewServlet.java:28)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1506)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:293)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
        at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:351)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:250)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:202)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:746)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:655)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:905)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:161)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:136)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:103)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:89)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:619)
Caused by: org.apache.catalina.fileupload.SizeException: The field text exceeds its maximum permitted  size of 10 characters.
        at org.apache.catalina.fileupload.RequestItemIterator$RequestItemImpl$1.raiseError(RequestItemIterator.java:144)
        at org.apache.catalina.fileupload.LimitedInputStream.checkLimit(LimitedInputStream.java:109)
        at org.apache.catalina.fileupload.LimitedInputStream.read(LimitedInputStream.java:166)
        at java.io.FilterInputStream.read(FilterInputStream.java:90)
        at org.apache.catalina.fileupload.Streams.copy(Streams.java:133)
        at org.apache.catalina.fileupload.Streams.copy(Streams.java:103)
        at org.apache.catalina.fileupload.Multipart.initParts(Multipart.java:129)
        ... 31 more

Partの取得でとまるようになっているようだ。らくちんだね。