ファイルアップロードのフォームでTextFieldなどの値が文字化けする

tapestry-uploadが壊れてます

MLのこの投稿で指摘されてるように、ファイルをアップロードしようとするとNullPointerExceptionが発生してしまいます。この投稿にあるように、springのCharacterEncodingFilterを使うと回避できます。

追記: Utf8Filterを、RequestFilterを実装するかわりにHttpServletRequestFilterを実装して、MultipartFilterの前に入れてやるのが正解っぽい。あとで詳しく書く。

http://d.hatena.ne.jp/butyricacid/20080105/1199519835

と書いたまま放置してしまっていましたが、というかMLに投稿してレスポンス付くの待ってたんだけど何もなくスルーされてしまった。。流量多くてすぐに流れちゃう上に文字コード絡みだと関心持つ人少ないからだろうか? 私の英文が通じてない?

ま、それはともかく詳しく書きます。


まず、ヌルポが出る問題自体は5.0.8ですでに直ってます。しかし、ファイルアップロードのフォームに他にフィールドがある場合、その値が文字化けします。Tapestry5Utf8Encoding の Utf8Filter を使っててもダメです(追記: wikiにアカウント作ってTapestry5Utf8Encodingに加筆してみました)。

そこで、tapestry-uploadの中を調べてみると次のことがわかりました。
ファイルアップロードのリクエストを処理する org.apache.tapestry.upload.internal.services.MultipartServletRequestFilter は org.apache.tapestry.services.HttpServletRequestFilter を実装していて、org.apache.tapestry.services.RequestFilter を実装している Utf8Filter よりも先に実行されてしまいます。そのためファイルアップロードのリクエストを処理する時点ではまだ、request.setCharacterEncoding("UTF-8") されておらず、request.getCharacterEncoding() はnullを返します。そしてこれがnullの時はフィールドの値はデフォルトエンコーディングでデコードするようになってます(5.0.7ではこのnullチェッックをしてなくてヌルポに)。

そんなわけで、以下のようにして Utf8Filter が MultipartServletRequestFilter より先に実行されるようにしたら文字化けしなくなりました。

  • Utf8Filter は RequestFilter のかわりに HttpServletRequestFilter を実装するように変更
  • その Utf8Filter を MultipartServletRequestFilter の前に追加
    public HttpServletRequestFilter buildUtf8Filter()
    {
        return new HttpServletRequestFilter()
        {
            public boolean service(HttpServletRequest request, HttpServletResponse response,
                    HttpServletRequestHandler handler) throws IOException
            {
                request.setCharacterEncoding("UTF-8");
                return handler.service(request, response);
            }
        };
    }

    public void contributeHttpServletRequestHandler(
            OrderedConfiguration<HttpServletRequestFilter> configuration,
            @InjectService("Utf8Filter")
            HttpServletRequestFilter utf8Filter)
    {
        configuration.add("Utf8Filter", utf8Filter, "before:MultipartFilter");
    }

MultipartServletRequestFilter は MultipartFilter という名前で登録されているので、それの「前」を明示的に指定して Utf8Filter を追加するために、"before:MultipartFilter" という引数を渡しています。