Apacheで2GB以上のファイルアップロードを扱う

久しぶりに記事を書いてみます。

今回はApache+Tomcatで2GB以上のファイルを扱う方法です。2GB以上のファイルがアップできないよーと言われたのが発端なのですが、まぁちょっと調べてみました。(2GB以上をアップする事なんて普通無いだろうと思うわけですが、それが結構あるんですよw)2GBの壁ということはsigned intのMAXという事になります。

問題になったシステムはSolaris10_x86(uいくつか忘れた。たぶん5くらい?w)にApache2.2.11+mod_proxy_ajp+Tomcat6.0.18 (JDK6u13)という構成です。原因はいくつかありますが、順番にチェックしていきました。

まずはログを調べてみたらTomcatがparseLongでエラーを吐いている。Content-Lengthの値がおかしいという事で、Tomcatにデバッガをしかけてチェック!。どうやら値がマイナス値のご様子。この時点でTomcatは悪くないという事に。(ちなみにTomcat6.0.13だとparseIntを使っているので注意)
次に簡単に調べられるところは、、、ApacheのログでContent-Lengthを出力するようにしてみた(${Content-Length}i)。見事にマイナス値。
この時点でおかしいのはApacheかブラウザという事に。

ブラウザが対応していないのではないか?説。

確かに検索をしてみるとブラウザによっては2GB以上のファイルはそもそもブラウザ側がPOST制限をかけている場合があるようです。その回避策としてはFLASHやJavaアプレットで分割させたりしましょうというらしい。で、問題になっているシステムはFlashでアップロードしていたのでした。どんな値を送っているのかな。と思ってWireSharkでパケットキャプチャ。ここでは見事に4GB以上のファイルも正の値で送信されていました。
原因はApacheにあり!

という事でApacheにログを仕込んでビルドしようと思ったけど、ビルドの時間を待つのが耐えられないのでソースを追ってみた。Content-Lengthでソースを検索すればそれっぽいのがいっぱい出てくる。server/util_script.cで処理しているみたい。さらにソースを読み進めていきます。

リクエストの情報はrequest_recという構造体に格納するようになっていて、Content-Lengthはそのclengthというメンバに格納される。この型はapr_off_t。これはsrclib/apr.hで定義されていてoff_tのtypedefである。で、off_tっていうのはlongで定義されているわけです。つまり32ビット!謎はとけました。

Solarisのマニュアルはこちら http://jp.sun.com/practice/software/solaris/jp/wp/Sol_file/sol_3.html

off_tを64ビット(long long)にするには、マクロを定義すればいいらしい。
Configure時にこうかな。「-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
本当は_FILE_OFFSET_BITS=64のみで良いらしいけど、一応前者もつけておく。

APR, APR-UTIL, Apacheをこのオプションつけてビルドすれば解決できそうです。
(ApacheだけあなくてAPRとAPR-UTILも必要ですよね・・?)

(追記)

家で見たソースはoff_tだったのに、問題のシステムのところのソースをみたらoff64_tで定義されてた。
あれー・・・。(´・ω・`)

新しいサイトもよろしくお願いします!