ソフトの砂場(ファイルのアップロード)

最終更新日:2000年10月22日

 最近DelphiプログラマのためのCGI入門関連で、ファイルのアップロードに関する質問が立て続けに数多くやってきています。「アップロードプログラムコンテスト」のようなものがあるのでしょうか(^_^)。せっかくなので、これをネタにうだうだと書いてみます。

 CGI関連にもいろいろあって、Windows-CGIならWebサーバが処理してくれるので、アップロードは何も考える必要はありません。今や使う人がほとんどいないかもしれませんが、O'ReillyのWebSiteを使ってみます。

<html>
<head><title>upload test</title></head>
<body>
アップロードテスト
<FORM ACTION="/cgi-win/uptest1.exe/PageProducer1" METHOD=POST ENCTYPE="multipart/form-data">
ファイル1:<INPUT TYPE=FILE NAME="upFileName1"><BR>
ファイル2:<INPUT TYPE=FILE NAME="upFileName2"><BR>
<INPUT TYPE=SUBMIT NAME="uploadButton">
</FORM>
</body>
</html>

のようなHTMLファイルを作っておき、これを使ってファイルをアップロードすると、iniファイルの[Form File]セクションは次のようになります。

[Form File]
upFileName1=[C:\WebSite\cgi-temp\99ws.000] 37445 image/jpeg binary [D:\temp\data1.jpg]
upFileName2=[C:\WebSite\cgi-temp\99ws.001] 29847 image/jpeg binary [D:\temp\data2.jpg]

 アップロードされた2つのファイルは、Webサーバがこのiniファイルに書かれている99ws.000と99ws.001というファイル名で自動的に保存します。後は、これらのファイルをアプリケーションで好きなようにすればいいわけです。


 では、ISAPIでどうなるかやってみましょ。上と同じようなHTMLファイルを作って(action=の値が異なりますが)、それを処理するプログラムとして、次のようなものを作りました。

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  upFile: TextFile;
begin
  AssignFile(upFile, 'd:\temp\uptest2.txt');
  try
    Rewrite(upFile);
    Write(upFile, Request.Content);
  finally
    CloseFile(upFile);
  end;
end;

 これで、小さいJPEGファイルとバイナリファイルをアップロードしてみると、uptest2.txtファイルは次のようになりました。

-----------------------------72732526810639
Content-Disposition: form-data; name="upFileName1"; filename="D:\TEMP\1s.jpg"
Content-Type: image/jpeg

(ここは、JPEGファイルの内容がそのまま(バイナリで)書かれている)
-----------------------------72732526810639
Content-Disposition: form-data; name="upFileName2"; filename="D:\TEMP\data2.dat"

(ここは、バイナリデータがそのまま(バイナリで)書かれている)
-----------------------------72732526810639
Content-Disposition: form-data; name="uploadButton"

Submit Query
-----------------------------72732526810639--

というわけで、後は境界で区切られたデータを、それぞれヘッダを切り捨ててファイルに書けば出来上がりです。と言いたいところですが、残念ながらRequest.Contentプロパティにデータが入りきらないことがあります。そこで、Request.ReadStringメソッドで残りのデータを読み出します。ちょろっと書いたプログラムは、こんな感じ。本当は、最初にどかっと領域を取っておいて、そこに読み出した方が効率がよいのですが、まあ動くのでよしとしましょう。

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  upFile: TextFile;
  s1, s2: string;
  readLen, remainLen: integer;
begin
  s1 := Request.Content;
  readLen := Length(Request.Content);
  remainLen := Request.ContentLength - readLen;

  while (remainLen > 0) do
  begin
    s2 := Request.ReadString(remainLen);
    readLen := Length(s2);
    if (readLen > 0) then
      s1 := s1 + s2;
    remainLen := remainLen - readLen;
  end;

  AssignFile(upFile, 'd:\temp\uptest2.txt');
  try
    Rewrite(upFile);
    Write(upFile, s1);
  finally
    CloseFile(upFile);
  end;
end;

 ここまでくれば、後はファイルに書くだけです。DelphiプログラマのためのCGI入門のアップロードの節で、ContentFieldに書いているところを、ファイルに書き出すようにすればいいですね。


ホーム