[11] [[媒体型]] [DFN[[CODE(MIME)[multipart/form-data]]]] は、 [[HTML]] の[[フォーム]]の[[提出]]のために設計された書式です。 名前から分かる通り、 [[MIME]] の [CODE(MIME)[[[multipart/[VAR[*]]]]]] の書式に基づいています。 元々は HTML のフォーム、特にファイルの[[うp]] ([[[CODE(HTMLe)[input]]/[CODE(HTML)[file]]]]) のために採用されましたが、 HTML 以外のフォームの提出にも使われています。 仕様上は任意の媒体型のデータを扱うことができます。 [[#comment]] * 仕様書 [21] 仕様書: - [[RFC 1867]] - [[RFC 2388]] [CITE[Returning Values from Forms: multipart/form-data]] - [[HTML 4]] -- [CITE[multipart/form-data]] -- [CITE[A.1.3 Minor typographical errors that were corrected]] -- HTML 4.01 正誤表 [CITE[10. content-disposition: attachment]] RFC 1867 と RFC 2388 と HTML 4 の [CODE(MIME)[multipart/form-data]] の規定は文章を流用していて同じようなことが書いてありますが、 少しずつ違います。独立の仕様書になっている RFC 2388 が当然一番詳しくなっています。 [7] [[HTML 4]] の [CODE(MIME)[multipart/form-data]] への言及の変遷: = 勧告以前の HTML 4 原案では、高々紹介程度で [[RFC 1867]] にほぼ丸投げ。 = HTML 4.0 勧告第1版: ''Forms in HTML documents'' = HTML 4.0 勧告第2版: ''Forms in HTML documents'' =- 勧告第1版と全く同内容 = HTML 4.01 勧告提案: ''Forms in HTML documents'' =- RFC 1867 から [[RFC 2388]] に参照先を変更 = HTML 4.01 勧告: ''Forms in HTML documents'' =- typo 修正 =- 例中の [CODE(MIME)[Content-Disposition: attachment]] を [CODE(MIME)[Content-Disposition: file]] に変更 = HTML 4.01 正誤表: ''HTML 4 Errata'' =- 勧告での [CODE(MIME)[attachment]] から [CODE(MIME)[file]] に再修正。しかも RFC 2388 に責任転嫁(藁 [22] [CODE(MIME)[multipart/form-data]] 内容は [[RFC 2045]] で説明された[[多部分]] MIME データ列の規則に従います。 [CODE(MIME)[multipart/form-data]] の定義は [[IANAREG]] から入手できます。 [SRC[HTML 4 17.13.4.2]] と書いてありますけど、むしろ [[RFC 2046]] を読むべきでしょう。 また、後方互換性, 他の内容型との関係, 効率の問題その他については [[RFC 1867]] [SRC[HTML 4.0 17.13.4.2]] ・ [[RFC 2388]] [SRC[HTML 4.01 17.13.4.2]] を読むよう指示があります。 ちなみに、仕様書の発行順序は HTML 4.0 → RFC 2388 → HTML 4.01 です。 [[#comment]] * 構文 [40] 基本的には、 [CODE(MIME)[[[multipart/mixed]]]] と同じ書式です。 [SRC[[[RFC 2046]], HTML 4 17.13.4.2, RFC 2388 3.]] [23] フォームの各欄は、応用とフォームによって定義された順で、 それぞれ [CODE(MIME)[multipart/form-data]] の[[本体部分]]とします。 [SRC[RFC 2388 4.1]] 本体部分の順序は、 RFC 2388 では規定されていません。 [SRC[RFC 2388 5.5」」 [41] HTML の場合、[CODE(MIME)[multipart/form-data]] の[[本体部分]]は、それぞれ、 [[成功]]制御子に対応します。順序は制御子の[[文書順]]とします。 [SRC[HTML 4 17.13.4.2]] [24] 多部分境界 ([CODE(MIME)[[[boundary]]]]) はデータ中に現れてはなりません。 [SRC[HTML 4 17.13.4.2, RFC 2388 4.1]] [26] ほかのすべての [CODE(MIME)[[[multipart/[VAR[*]]]]]] 型と同様、 各本体部分は省略可能な [CODE(MIME)[[[Content-Type]]]] 頭欄を持ちます。省略時の既定値は [CODE(MIME)[[[text/plain]]]] です。 媒体型が分かっている場合は適当に札付けし、分からない場合は [CODE(MIME)[[[application/octet-stream]]]] とするべきです。 [SRC[RFC 2388 4.1]] HTML [[UA]] は、 [CODE(MIME)[Content-Type]] 欄を ([CODE(MIME)[[[charset]]]] 引数を含めて) 供給するべきです。 [SRC[HTML 4 17.13.4.2]] [27] 各本体部分は [ABBR[[[CTE]]][[CODE(MIME)[[[Content-Transfer-Encoding]]]]]] を使ってもかまいません。 [SRC[HTML 4 17.13.4.2, RFC 2388 3., RFC 2388 4.3]] その他 MIME の機構により暗号化・圧縮などをしても構いません。 それは [CODE(MIME)[multipart/form-data]] を生成する応用の機能です。 [SRC[RFC 2388 5.1]] [[#comment]] ** 改行 [8] MIME の規定により、境界行や実体頭欄の末端の改行は [CODE(char)[[[CRLF]]]] でなければなりません。 [CODE(char)[[[CR]]]] や [CODE(char)[[[LF]]]] だけではいけません。 [25] 他のすべての MIME 転送同様、改行は [CODE(char)[[[CRLF]]]] とします [SRC[HTML 4 17.13.4.2]]。 と HTML 4 も言っています。 ([Q[転送]]とはどこからどこまでか、 曖昧であるのが問題ではありますが。) [9] >>8 は大前提なんですが、 一方で HTTP ではいい加減な実装が多いので、もしかしたら・・・ [CODE(char)[CR]] だけとか [CODE(char)[LF]] だけとかで送ってくる糞 UA もあったりするんでしょうか? 多分 [[Mozilla]] とか [[Opera]] とか [[IE]] とかの有名どころは大丈夫だと思うんですが。。。 [[#comment]] ** 欄名 (制御子名) [28] 各欄は名前を持ちます。名前はフォーム内で固有です。 [SRC[RFC 2388 3.]] 欄名が同じ本体部分が複数あるときの取扱いは RFC 2388 では規定されていません。 [SRC[RFC 2388 5.]] 応用により、例えば HTML では[[フォーム・データ集合]]に同じ名前の制御子名があれば、 複数の本体部分が同じ名前となることがあります。 各本体部分は、 [CODE(MIME)[[[Content-Disposition]]]] を [CODE(MIME)[[[form-data]]]] とし、その [CODE(MIME)[[[name]]]] 引数に対応する制御子の欄名 ([[制御子名]]) を指定します [SRC[HTML 4 17.13.4.2, RFC 2388 3.]]。 [29] 非 [[ASCII]] 文字を含むときには、 [[RFC 2045]] で説明されている方法で符号化して構いません [SRC[HTML 4 17.13.4.2]]。と書いてはあるのですが、 [Q[構いません]]ではなくて何らかの方法で符号化しなければ'''なりません''' (MIME 頭欄は ASCII と定義されています)。さて、 RFC 2045 の一体どこで [CODE(MIME)[Content-Disposition]] [CODE(MIME)[name]] を符号化する方法が説明されているのでしょうか? されていません。 詳しくは [CODE(MIME)[[[name]]]] 引数の説明をご覧ください。 相当する部分は、 RFC 2388 では [[RFC 2047]] を参照しています [SRC[RFC 2388 3., 5.4]]。 RFC 2047 と言うからには [CODE(ABNF)[[[encoded-word]]]] を使うのでしょう。普通 [CODE(ABNF)[[[quoted-string]]]] でが [CODE(ABNF)[encoded-word]] は使わない (使えない) ものですが、 明示的に 2047 を参照しているのですから [CODE(MIME)[name]] 引数では特別に使えるのでしょう。 [30] 例 [SRC[HTML 4 17.13.4.2]] [PRE(MIME)[ Content-Disposition: form-data; name="mycontrol" ]PRE] この例では、制御子名 [SAMP[mycontrol]] を表します。 この欄を含む本体部分の[[本体]]は、この名前の制御子の[[現在値]] (またはファイル内容) になります。 [[#comment]] ** 本体 [31] 各本体部分の[[本体]]は、ファイル選択制御子 ([[[CODE(HTMLe)[input]]//[CODE(HTML)[file]]]]) ではファイルの内容、 それ以外では[[現在値]]になります。 [WEAK[(という説明が HTML 4 仕様書ではきちんとなされていません。)]] [[#comment]] ** ファイルの場合 [32] 本体にファイルの内容を入れる場合には、適当な[[媒体型]]か、 分からなければ [CODE(MIME)[[[application/octet-stream]]]] を指定するべきです。 複数のファイルが1つの項目で選択されて提出される場合には、 [CODE(MIME)[[[multipart/mixed]]]] が一つの本体部分に埋められます。 [SRC[HTML 4 17.13.4.2, RFC 2388 3., RFC 2388 4.2]] [44] フォーム・ソフトウェアはファイル名やその他のファイルの属性情報をつけても構いません。 [SRC[RFC 2388 4.4]] [33] HTML UA は提出する各ファイルにファイル名を供給するよう試みるべきです。 ファイル名は [CODE(MIME)[Content-Disposition]] 欄の [CODE(MIME)[[[filename]]]] 引数で指定します。[SRC[HTML 4 17.13.4.2]] UA 側システムのファイル名が [[US-ASCII]] でないときには、 ファイル名は近似するか、 RFC 2045 の方法で符号化しなければなりません。 [SRC[HTML 4 17.13.4.2]] と >>29 に続いてここでも [Q[RFC 2045の方法]]が出てきましたが、 こちらもやはり RFC 2045 に規定はありません。 RFC 2388 は、 RFC 2045 ではなく、 [[RFC 2231]] の方法を使っても良いとしています。 [SRC[RFC 2388 4.4]] この規定は RFC 2231 とは整合していますが、 [CODE(ABNF)[encoded-word]] を使うべしとする [CODE(MIME)[name]] 引数の規定 (>>29) とは矛盾しています。本当に使い分けろというのでしょうか。 [45] 提出するファイルは相互にファイル名で参照関係を持っているかもしれませんから、 ファイル名が保存されていると便利です。 [SRC[HTML 4 17.13.4.2, RFC 2388 4.4]] ファイル名指定に関する様々な問題については、 [CODE(MIME)[[[filename]]]] 引数の説明をご覧ください。 [34] 提出ファイルが複数の時の [CODE(MIME)[multipart/mixed]] 内の本体部分では [CODE(MIME)[Content-Disposition: file]] とするかのような記述が仕様書にあります [SRC[HTML 4.01 17.13.4.2]] が、 [CODE(MIME)[attachment]] の誤りだそうです [SRC[HTML 4.01 正誤表 10.]]。 [WEAK[みっともないことに HTML 4.01 正誤表は [[RFC 2388]] に責任転嫁しております(w。確かに元々 HTML 4.0 では [CODE(MIME)[attachment]] になっておりましたが、 HTML 4.01 で [Q[minor typo]] として修正されています [SRC[HTML 4.01 A.1.3]]。]] [[#comment]] ** 遠隔ファイル指示子 [49] 遠隔ファイルを直接送らずに、 [CODE(MIME)[[[message/external-body]]]] を使ってその指示子だけを送ることができます。 [SRC[RFC 2388 5.3]] [[#comment]] * 他との関係 [6] HTML のフォームでは [CODE(MIME)[[[application/x-www-form-urlencoded]]]] もよく使われていますが、任意のバイナリ・データや非 ASCII 文字を効率よく確実に扱うことができないという問題があります。 バイナリ・データや非 ASCII 文字を含むフォームの提出では、 [CODE(MIME)[multipart/form-data]] を使うべきです [SRC[HTML 4 17.13.4.2]]。 ファイル選択制御子 ([[[CODE(HTMLe)[input]]/[CODE(HTML)[file]]]]) を使う時には、 [CODE(MIME)[multipart/form-data]] を [CODE(HTMLe)[form]] の [CODE(HTMLa)[enctype]] で指定するべきです [SRC[HTML 4 17.3, 17.13.4.2]]。 [35] HTML のフォームで [CODE(MIME)[multipart/form-data]] で提出させたい時は、 [CODE(HTMLe)[[[form]]]] 要素の [CODE(HTMLa)[[[enctype]]]] 属性に [CODE(MIME)[multipart/form-data]] と指定しておきます。 各本体部分の文字符号化方式の決定には、 [CODE(HTMLe)[form]] 要素の [CODE(HTMLa)[[[accept-charset]]]] 属性の指定を参照します。 [[#comment]] * 安全性 [50] [CODE(MIME)[multipart/form-data]] を構成するプロトコル要素や[[フォーム]]の仕組み自体には、 様々な安全上の問題があることが知られています。 例えば、利用者の意図しない状態や利用者が十分な考慮を行えない状況で自動的・ 半自動的にフォームを提出させると、 利用者の私的な情報や利用者の環境の安全に関わる情報が送信されてしまう虞があります。 このほかにも、フォームの提出という仕組みそのものに起因する問題が多く見つかっています。 また、ファイルを提出する際には [CODE(MIME)[filename]] 引数を使うことができますが、フォーム処理エージェント ([CODE(MIME)[multipart/form-data]] を処理する側) が信頼して無防備に実際のファイル名等として使用すると、 既存の別のファイルやシステム・ファイルを上書きしたり、 その環境で扱えないファイル名のファイルが中途半端にできてしまったりする虞があります。 詳しくは [CODE(MIME)[[[filename]]]] 引数の説明をご覧ください。 このようなフォーム自体や [CODE(MIME)[multipart/form-data]] が利用しているプロトコル要素に関する問題や、 特定の実装に依存した問題を除いては、 [CODE(MIME)[multipart/form-data]] に関する安全上の問題は見つかっていません。 [[#comment]] * 適合性 [10] HTML 4 UA は、 [CODE(MIME)[multipart/form-data]] によるフォームの提出を実装しなければなりません [SRC[HTML 4 17.13.4]]。 [[#comment]] * 実装 [15] WinIE 3.02 用の file upload add-on は1997年の中ごろに出ました。 [36] 現代のほとんどの [[WWWブラウザ]]は [CODE(MIME)[multipart/form-data]] によるフォームの提出を実装しています。 [37] 一方、 [[CGIスクリプト]]などの鯖側は酷い状況です。 多くの実装は相手にもしていません。 [[Perl]] なら [CODE(file)[[[CGI.pm]]]] などを使えば自動的に対応できますが、[WEAK[最近は増えてきたとはいえ]]モジュールを CGI スクリプトで使うことは少なく、 [CODE(MIME)[application/x-www-form-urlencoded]] にしか対応していません。ファイルのうpがしたくなったら (素直にモジュールを使えばいいのに) 見よう見まねで適当に対処しようとして、 結局特定ブラウザの特定の版でしか上手く動かないようなコードを書いてみたり。 [WEAK[(で、質問掲示板で暴れてみたり。)]] お前らちゃんと仕様書読んでくださいよ。 処理系で標準または標準に近いモジュール的なものが[[要求]]の解析をしてくれることが広く知られていて、 そのモジュール的なものの作者がちゃんと仕様を読んでコードを書くような人なら、 その処理系で書かれた処理はさほど深く考えなくても自動的に [CODE(MIME)[multipart/form-data]] を正しく処理できるはずです。 [WEAK[よく知りませんけど、 Java servelet とか PHP はその辺きちんとしてるのではないですか?]] [[#comment]] ** 媒体型と charset [1] [[WinIE]] も [[Mozilla]] も [[Opera]] も、 [CODE(MIME)[multipart/form-data]] に含まれる[[本体部分]]には [[charsetパラメーター]]を付けてくれません。 (ファイル送信を除いて [CODE(MIME)[[[Content-Type]]]] 欄そのものをつけません。) [2] Opera は、 [CODE(MIME)[multipart/form-data]] そのものに存在しない [CODE(MIME)[charset]] 引数をつけてきます。 この charset 値は実際にはそれに含まれる本体部分の[[実体本体]]及び [CODE(MIME)[[[Content-Disposition]]]] 欄の [CODE(MIME)[[[name]]]] 引数に適用されるようです。あ、 [CODE(MIME)[[[filename]]]] にもかな? 今度確かめてみよう。 [3] 規格不適合ながらもとりあえず >>2 のように情報を送ってくる Opera に対して、 WinIE と Mozilla は既定では何もしません。ただし、 [CODE(HTML)[[[_charset_]]]] hack を使えば一応は情報を得られます。 [4] >>2-3 の情報は、 [CODE(HTML)[[[file]]]] として送られる実体本体には適用できません。 (その実体の頭欄には適用されます。) [CODE(MIME)[charset=[[x-unknown]]]] と考えるしかなさそうです。問題は、 一般の form data と file を区別する確実な方法がないことです。 IE, Moz, Opera に限れば、 [CODE(MIME)[filename]] 引数の有無で決定できますが。。。 [5] >>4 あ、確実な方法が1つだけあります。受取る側が名前を知っていること。 これ超確実。 [[#comment]] ** 複数ファイルをまとめて提出 [13] 1つのファイル選択制御子 ([[[CODE(HTMLe)[input]]/[CODE(HTML)[file]]]]) を使って複数ファイルをうpする (>>#) のは、 [[UA]] で対応してるのはなさげ、 サーバーもおそらく全滅だろうという感じですね。 [[www-html]] で [[Opera]] の特定の版では出来るという未確認情報がありましたが、 最新版では出来ないらしいし、勘違いかなんかじゃないかなあ。 [[#comment]] ** 零個のファイルを提出 [6] ファイル選択制御子 ([[[CODE(HTMLe)[input]]/[CODE(HTML)[file]]]]) があっても、ファイル名として何も指定されなかった場合、 WinIE も Mozilla も Opera も、空の内容を送ります。 このとき、 WinIE と Mozilla は頭欄に [CODE(MIME)[Content-Type: [[application/octet-stream]]]] と書いてきて、 [CODE(MIME)[Content-Disposition]] にも [CODE(MIME)[filename=""]] がつきます。 Opera ではどちらもつかず、本当に空 ([CODE(MIME)[Content-Disposition: [[form-data]]; name=[VAR[名前]]]] と空の内容だけ) になります。 [38] 空の実体を送ってしまうと (一般の UA の場合に) ファイル未選択と内容が空のファイルが区別できなくなってしまいます。 ファイルを選択していないファイル選択制御子はそもそも[[成功]]にしてはいけないのではないでしょうか。 [[#comment]] * 例 [39] HTML のフォームの例 [SRC[HTML 4 17.13.4.2、改]] [PRE(HTML)[

What is your name?
What files are you sending?

]PRE] 文章入力欄に [SAMP[Larry]] と記入し、ファイル選択で [SAMP(file)[file1.txt]] を指定して提出した場合 [SRC[HTML 4 17.13.4.2]]: [PRE(MIME)[ Content-Type: multipart/form-data; boundary=AaB03x --AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files"; filename="file1.txt" Content-Type: text/plain [VAR[... contents of file1.txt ...] --AaB03x-- ]PRE] 加えて [SAMP(file)[file2.gif]] も選択していた場合 [SRC[HTML 4 17.13.4.2]]: [PRE(MIME)[ Content-Type: multipart/form-data; boundary=AaB03x --AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files" Content-Type: multipart/mixed; boundary=BbC04y --BbC04y Content-Disposition: [DEL[file]] [INS[attachment]]; filename="file1.txt" Content-Type: text/plain [VAR[... contents of file1.txt ...]] --BbC04y Content-Disposition: [DEL[file]] [INS[attachment]]; filename="file2.gif" Content-Type: image/gif Content-Transfer-Encoding: binary [VAR[...contents of file2.gif...]] --BbC04y-- --AaB03x-- ]PRE] [47] ユーロ通貨記号を値に使った例 [SRC[RFC 2388 4.5、改]] [PRE(HTML)[ Content-Type: multipart/form-data; boundary="AaB03x" --AaB03x content-disposition: form-data; name="field1" content-type: text/plain;charset=windows-1250 content-transfer-encoding: quoted-printable Joe owes =80100. --AaB03x-- ]PRE] [[#comment]] * メモ