GroovyServletでファイルアップロードを処理させようとしたところ、思いっきり落とし穴に嵌りましたのでメモ。
時間がない方向けの結論:
- GroovyServletでは、内部でHttpServletRequestのパラメータを読みだして独自にBindingしている。
- → このため、HttpServletRequest.getInputStream()を(恐らくContainer側の実装にも依ると思うが)GroovyServletの内部処理で読みきってしまう。
- → GroovyServlet上で動作するスクリプトからは、(既に読みきってしまっているため)HttpServletRequest.getInputStream()からHTTPリクエストのBODYデータを読み込めない。
- (もしかしたらInputStream.reset()を呼べば良いかもしれないが、関連トピックで調べてみたところあまりreset()を使う解法が無かったっぽいので、駄目なのかもしれない。)
- 解法:Servletを呼ぶ前に独自のFilterでgetInputStream()からbyte[]型に読み出し、それをHttpServletRequestWrapper派生の独自ラッパーで包み込んだのを本体Servletに渡す。
- これにより、独自ラッパー内のbyte[]型として何回でもByteArrayInputStream経由で読み出せるようになる。
- 多分GroovyServletとの副作用も無いっぽい。
- Apache Commons FileUpload も使えたので、多分これで行けるだろう。実際好きなタイミングでリクエストボディ取り出せるのは便利。
- この時用意するFilterとかラッパーについては以下の詳細説明を参照してください。
用意するFilterやラッパーについては、以下のBlogが詳しいです。
- HonestyWorks TechNote » slim3のcontrollerでServletInputStreamを使いたいとき
- Slim3 GAE/J で request getInputStream の中身がない|あきらるのブログ
- ServletInputStreamを2回使う - Usa*Usa日記
GroovyServletのサンプルにも組み込んでいます。
以下、落とし穴に嵌ったのを認識する前段階からの足跡。
1.Apache Commons FileUploadを使おうと、練習にまずはHttpServletRequest.getInputStream()を使ってみる。
- @IT:Java TIPS -- ファイルアップロード処理を簡単にする(Commons活用)
・・・が、getInputStream()しても正常に取れない・・・。
2.自分が嵌るくらいだから、既に誰か対処法見つけてるんじゃないの?
- groovyあれこれ: groovletsでファイルアップロードする
- Add File Upload Support to Groovlets in Google App Engine - Messages from mrhaki
・・・できてるようではあるが・・・記事も古く、バージョンも大分古いので内部動作も変わってしまってるのかも。
3.ようやく、ServletAPIを確認しようとひっくり返してみる。
これでようやく、getParameter系とかgetReader/getInputStreamって併用すると副作用ある感じ?ということに気づく。
4.getParameter系呼んだ後にgetInputStream()呼び出してトラブった人いないかな?
- getReaderとgetParamater系って一回のリクエストで同時に使えないの?について調べてみた - 生き急げるほど意志は強くない
- [#GROOVY-895] Groovlets: File upload support - jira.codehaus.org
- groovy - user - Re: GroovyServlet - Cannot Read ServletInputStream
どうもPOSTの場合にgetParameter系呼ぶと、リクエストボディのストリーム(?)を読みきってしまい、その後にgetInputStream()を読んでも、既に読みきってしまってるため手遅れ状態っぽい・・・。
→最終的にGroovyServletの中身(=Groovyのソースコード)をたどったところ、GroovyServletのBinding中でgetParamXXXX()してるためか、その影響でgetInputStream()に副作用が生じているものと思われます。
プレーンテキスト形式でダウンロード