Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2008年11月11日] [更新 2017年04月03日] Delphi サンプルプログラム集
   
772_TIdHTTP を使用したダウンロード
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro + Indy 10 (Ver 10.5.7)
772_Indy_HTTPGet.zip [6,378 KB] 2018年11月02日第2版 (EXE 同梱)







TIdHTTP を使用したダウンロードのサンプルコード


Delphi に附属している Indy というインターネット関係を操作するためのコンポーネントの TIdHTTP を使用したサンプルです.主に HTTP サーバからファイルをダウンロードするプログラムです.





01_URL ( URI ) 文字列の分解


URL の文字列を扱う場合,パス名,ファイル名を別々に取出したいことがあります.TIdURL というクラスを使用して,元の URLから下図のような結果を取出すことができます.

TIdHTTP コンポーネントを使用している場合は,URI プロパティから取得できます.URI プロパティの型は TIdURL となっています.



図1
設計時画面
図2
実行画面

リスト1
UR L文字列をバス等に分解 (HandleRedirects プロパティ使用)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    LabeledEdit1 : TLabeledEdit;
    LabeledEdit2 : TLabeledEdit;
    LabeledEdit3 : TLabeledEdit;
    LabeledEdit4 : TLabeledEdit;
    LabeledEdit5: TLabeledEdit;
    LabeledEdit6: TLabeledEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private 宣言 }
    IniFileName  : String;
    FileSize     : Integer;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses IdURI;

{$R *.dfm}

//=============================================================================
//  EditにURI(Uniform Resource Identifier)をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  LabeledEdit1.Text := 'http://mrxray.on.coocan.jp/Delphi/plSamples/index.htm';
end;

//=============================================================================
//  URL(URI)をプロトコルやホスト文字列に分解する
//
//  TIdURIにはusesにIdURIが必要
//  IdURIはコンポーネントではなくクラス
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  uri: TIdURI;
begin
  //LabelEdit1にURLを入力しておく
  uri := TIdURI.Create(LabeledEdit1.Text);
  try
    LabeledEdit2.Text := uri.Protocol;
    LabeledEdit3.Text := uri.Host;
    LabeledEdit4.Text := uri.Port;
    LabeledEdit5.Text := uri.Path;
    LabeledEdit6.Text := uri.Document;
  finally
    uri.Free;
  end;
end;

end.




02_HTTP サーバファイルの情報 ( HandleRedirects プロパティ )


HTTP サーバ上のファイルのサイズ,最終更新日等の情報を取得するサンプルです.
HandleRedirects プロパティを True にして,URL のリダイレクトを行います.例えば,CGI 経由でダウンロードするようになっている場合でも,実際のファイル名を取得することができます.リダイレクトの回数はデフォルトで15回となっています.


図3
設計時画面
図4
実行画面

リスト2
HTTP サーバ上のファイルの情報を取得 (HandleRedirects プロパティ使用)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdHTTP;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    IdHTTP1: TIdHTTP;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用のURLをセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  Memo1.Lines.Clear;
  Edit1.Text := 'http://mrxray.on.coocan.jp/index.htm';
end;

//=============================================================================
//  指定URLのヘッダ情報を取得
//
//  リダイレクト処理を行っているので,例えばCGI経由のファイルも情報取得可能
//  (ただし少し時間がかかる)
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  URL : String;
  Ret : Boolean;
begin
  Memo1.Lines.Clear;

  Screen.Cursor := crHourGlass;
  URL := Trim(Edit1.Text);
  //リダイレクト処理をTrueに
  IdHTTP1.HandleRedirects := True;

  try
    //このURLが存在しないと,Headメソッド実行でエラーとなる
    //リダイレクト実行後に,URLにリダイレクト先のURLが取得できる
    IdHTTP1.Head(URL);
    Ret := True;
  except
    if IdHTTP1.ResponseCode = 302 then begin
      //リダイレクト後のURLで再度Headメソッドを実行して情報取得
      IdHTTP1.Head(IdHTTP1.Response.Location);
      Ret := True;
    end else begin
      Ret := False;
    end;
  end;

  if Ret then begin
    Memo1.Lines.Add(IdHTTP1.ResponseText);
    Memo1.Lines.Add(IdHTTP1.Response.RawHeaders.Text);
    Memo1.Lines.Add('');
  end else begin
    //エラー時の処理
    MessageDlg('指定したページまたはファイルが見つかりませんでした.',
               mtInformation,
               [mbOK],0);
  end;

  IdHTTP1.Disconnect;
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  指定URLの更新日時とファイルサイズを取得
//
//  リダイレクト処理を行っているので,例えば
//  リダイレクト処理を行っているので,例えばCGI経由のファイルも情報取得可能
//  (ただし少し時間がかかる)
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  URL      : String;
  LastDate : TDateTime;
  DateStr  : String;
  FileSize : Longint;
  Ret      : Boolean;
begin
  Memo1.Lines.Clear;

  Screen.Cursor := crHourGlass;
  URL := Trim(Edit1.Text);
  //リダイレクト処理をTrueに
  IdHTTP1.HandleRedirects := True;

  try
    //このURLが存在しないと,Headメソッド実行でエラーとなる
    //リダイレクト実行後に,URLにリダイレクト先のURLが取得できる
    IdHTTP1.Head(URL);
    Ret := True;
  except
    if IdHTTP1.ResponseCode = 302 then begin
      //リダイレクト後のURLで再度Headメソッドを実行して情報取得
      IdHTTP1.Head(IdHTTP1.Response.Location);
      Ret := True;
    end else begin
      Ret := False;
    end;
  end;

  if Ret then begin
    //ファイルサイズ
    FileSize := IdHTTP1.Response.ContentLength;
    //最終更新日時
    LastDate := IdHTTP1.Response.LastModified;
    DateStr  := FormatDateTime('yyyy年mm月dd日 hh時nn分', LastDate);

    Memo1.Lines.Add('最終更新日時   : ' + DateStr);
    Memo1.Lines.Add('ファイルサイズ : ' + IntToStr(FileSize) + ' バイト' );
    Memo1.Lines.Add('フルパス名     : ' + IdHTTP1.URL.URI);
    Memo1.Lines.Add('ファイル名     : ' + IdHTTP1.URL.Document);
    Memo1.Lines.Add('');
  end else begin
    //エラー時の処理
    MessageDlg('指定したページまたはファイルが見つかりませんでした.',
               mtInformation,
               [mbOK],0);
  end;

  IdHTTP1.Disconnect;
  Screen.Cursor := crDefault;
end;

end.




03_HTTP サーバファイルの情報 ( Redirect イベント )


次のコードもHTTPサーバ上のファイルの情報を取得するサンプルです.こちらは HandleRedirects プロパティではなく,Redirect イベントを使用しています.リダイレクトする度にその値を取得したい場合の参考になります.


図5
設計時画面
図6
実行画面

IdHTTPのResponseText プロパティ (読出し専用) は,以下のいずれかの値です.これらの HTTP サーバから応答で,301, 302 (HTTP1.1 では 307) の場合,多くのブラウザでは自動的にこのコードを認識して,リダイレクト先の URL を読みに行くようになっているようです.


100 Continue
クライアントは処理を続けるべきである
サーバがこのコードをサポートしていな場合は,ヘッダに 100-continue を付加する
101 Switching Protocols
サーバは完全なリクエストを受信したがまだ処理は完了していない
処理に時間がかかる場合に返されるコード
200 OK
リクエストは成功した
201 Created
メソッドによりリソースが生成された場合のコード
202 Accepted
リクエストは受け付けたがリソースの生成は完了していない.リクエストは拒否される可能性がある
203 Non-authoritative Information
200 に同じ.ローカルまたはプロクシからの応答の場合に使用する
204 No Content
リクエストは成功したが,返すべき内容はない
ブラウザ等からのリクエストの場足,レスポンスを受信しても表示は更新されない
205 Reset Content
リクエストは成功した.現在の画面をリセットすべきである
206 Partial Content
サーバはリソースに対する部分的GETリクエストを受け入れた
301 Moved Permanently
指定した URL は移動等によって存在しない.URL の間違い場合等 (最後の / がない等)
Location で示されるリダイレクト先に変更する必要があることもある
302 Moved Temporarily
HTTP1.1 では 307 に変更された
ページが一時的に別の場所に移動した
PHP によるリダイレクトの場合このコードが返される
303 See Other
リクエストに対するレスポンスは別の URL から発見できる
304 Not Modified
クライアントは条件付きGETを実行し,アクセスは許可されたが,更新されていない
305 Use Proxy
リクエストは Location で与えられるプロキシ URL を通してアクセスしなければならない
307 Temporary Redirect
302参照
400 Bad Request
リクエストの構文が間違っているためサーバは理解できなかった
401 Unauthorized
リクエストはユーザ認証を必要とする
多くのブラウザでは最初は 401 を返さずにユーザ認証入力用のダイアログが現れる
403 Forbidden
サーバはリクエストを理解したが拒否した.認証も役に立たない
サーバは拒否の理由を明らかにする必要はない.代わりに404を返してもよい
404 Not Found
リクエストの URI を見つけられなかった
サーバはその状態が一時的であるか恒久的であるか明らかにする必要 (義務) はない
405 Method Not Allowed
リクエストされた URI にあるリソースは使用されたメソッドを許可していない
406 Not Acceptable
リクエストの Accept ヘッダで指定しなければならない
407 Proxy Authentication Required
クライアントは最初にプロクシのユーザ認証が必要である
408 Request Timeout
サーバの待ち時間にリクエストが終了しなかった
例えばリクエストの終端に CRLF がない場合等
409 Conflict
リソースが現在の状態と矛盾するためリクエストを完了できなかった
指定ディレクトリが存在しない場合等.WebDAV のレスポンスでみられるコード
410 Gone
リクエストされたリソースはサーバ上に完全にない.サーバが利用できない
411 Length Required
リクエストにContent-Lengthヘッダがない場合
412 Precondition Failed
リクエストで与えられた条件が適切でない
413 Request Entity To Long
リクエストの処理可能サイズを超えている.この大きさはサーバの設定による
414 Request-URI Too Long. 256 Chars max
リクエストで与えた URI 文字列が長すぎる.このサイズはサーバの設定に依存する
415 Unsupported Media Type
サーバがサポートしていない.サーバに依存する
500 Internal Server Error
サーバ内部のエラー.例えばCGIスクリプトのエラー
501 Not Implemented
サーバはリクエストの機能をサポートしていない
405は機能をサポートしても許可していない場合
502 Bad Gateway
ゲートウェイであるプロクシが実行アプリケーションから不正なレスポンスを受取った
503 Service Unavailable
サーバの都合により処理できない.メンテナンスか過負荷である場合等
504 Gateway Timeout
ゲートウェイであるプロクシが実行アプリケーションからレスポンスが受信できなかった
505 HTTP Version not supported
サーバはクライアントの要求した HTTP のバージョンをサポートしていない
Indy はこのエラーを避けるために送信に HTTP1.0 プロトコルを使用している


リスト3
HTTP サーバ上のファイルの情報を取得 (Redirect イベント使用)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdHTTP;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    IdHTTP1: TIdHTTP;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure IdHTTP1Redirect(Sender: TObject; var dest: string;
      var NumRedirect: Integer; var Handled: Boolean; var VMethod: string);
  private
    { Private 宣言 }
    RealUrl : String;
    function CheckHTTPUrl(var AUrl:String): Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用のURLをセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  Memo1.Lines.Clear;
  Edit1.Text := 'http://mrxray.on.coocan.jp/index.htm';
end;

//=============================================================================
//  指定URLのヘッダ情報を取得
//
//  リダイレクト処理を行っているので,例えばCGI経由のファイルも情報取得可能
//  (ただし少し時間がかかる)
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  URL : String;
begin
  Memo1.Lines.Clear;
  Screen.Cursor := crHourGlass;
  URL :=Trim(Edit1.Text);

  if CheckHTTPUrl(URL) = False then begin
    MessageDlg('指定したページまたはファイルが見つかりませんでした.',
               mtInformation,
               [mbOK],0);
    Screen.Cursor := crDefault;
    exit;
  end;

  //情報の再取得
  //リダイレクトされた場合はURLはりダイレクト先
  IdHTTP1.Head(URL);

  Memo1.Lines.Add(IdHTTP1.ResponseText);
  Memo1.Lines.Add(IdHTTP1.Response.RawHeaders.Text);
  IdHTTP1.Disconnect;
  Memo1.Lines.Add('');

  IdHTTP1.Disconnect;
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  指定URLの更新日時とファイルサイズを取得
//
//  リダイレクト処理を行っているので,例えばCGI経由のファイルも情報取得可能
//  (ただし少し時間がかかる)
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  URL      : String;
  LastDate : TDateTime;
  DateStr  : String;
  FileSize : Longint;
begin
  Memo1.Lines.Clear;
  Screen.Cursor := crHourGlass;
  URL := Trim(Edit1.Text);

  if CheckHTTPUrl(URL) = False then begin
    MessageDlg('指定したページまたはファイルが見つかりませんでした.',
               mtInformation,
               [mbOK],0);
    Screen.Cursor := crDefault;
    exit;
  end;

  //情報の再取得
  //リダイレクトされた場合はURLはりダイレクト先
  IdHTTP1.Head(URL);

  FileSize := IdHTTP1.Response.ContentLength;
  LastDate := IdHTTP1.Response.LastModified;

  //最終更新日時
  DateStr  :=FormatDateTime('yyyy年mm月dd日 hh時nn分', LastDate);
  Memo1.Lines.Add('最終更新日時   : ' + DateStr);
  Memo1.Lines.Add('ファイルサイズ : ' + IntToStr(FileSize) + ' バイト' );
  Memo1.Lines.Add('フルパス名     : ' + IdHTTP1.URL.URI);
  Memo1.Lines.Add('ファイル名     : ' + IdHTTP1.URL.Document);
  Memo1.Lines.Add('');

  IdHTTP1.Disconnect;
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  引数のAUrlが存在するかチェックする関数
//  RealUrlはグローバル変数.OnRedirectイベントとの連携で取得する
//  成功すればAUrlにリダイレクト先(なければ元のAUrlはそのまま)を返す
//=============================================================================
function TForm1.CheckHTTPUrl(var AUrl: String): Boolean;
begin
  RealUrl := Aurl;
  try
    IdHTTP1.Head(RealUrl);
    AUrl   := RealUrl;
    Result := True;
  except
    AUrl   := '';
    Result := False;
  end;

  //リダイレクトされている場合はエラーとしない
  if IdHTTP1.ResponseCode = 302 then begin
    AUrl   := RealUrl;
    Result := True;
  end;
  Memo1.Lines.Add(RealUrl);
end;

//=============================================================================
//  IdHTTPのOnRedirect(リダイレクト)イベント
//  グローバル変数のRealUrlにリダイレクト先のアドレスを取得してIdHTTPのコマンド
//  の再実行を行うようにしている
//  リダイレクトの回数は,RedirectMaxmum(デフォルトでは15)で設定する
//
//  [参考]
//  リダイレクト(サーバからの応答)のステータスコード
//  301 Moved Permanently -- ページの引越しで完全にない
//  302 Moved Temporarily(HTTP1.1では307に変更?) -- 別の場所にある
//  HTTP/1.1 404 Not Found.の場合はリダイレクトを中止する
//=============================================================================
procedure TForm1.IdHTTP1Redirect(Sender: TObject; var dest: string;
  var NumRedirect: Integer; var Handled: Boolean; var VMethod: string);
begin
  if Pos('404.htm', dest) > 0 then begin
    Handled := False;
    RealUrl := '';
  end else begin
    RealUrl := dest;
    Handled := True;
  end;
end;

end.




04_HTTP サーバからファイルをダウンロード


HTTP サーバ上の指定ファイルを,GET メソッドでダウンロードするサンプルです.
ダウンロードするファイルがテキストファイルの場合は,文字のコードページに注意します.適切なコードページに変換しないと,ダウンロードしたテキストが文字化けが発生します.


図7
設計時画面
図8
ダウンロード中

リスト4
HTTP サーバ上のファイルをダウンロード
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdHTTP, StdCtrls, ExtCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    IdHTTP1: TIdHTTP;
    Button1: TButton;
    Memo1: TMemo;
    Panel2: TPanel;
    Panel3: TPanel;
    LabeledEdit1: TLabeledEdit;
    ProgressBar1: TProgressBar;
    SaveDialog1: TSaveDialog;
    Panel1: TPanel;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus;
      const AStatusText: String);
    procedure IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;
      AWorkCount: Int64);
    procedure IdHTTP1WorkBegin(Sender: TObject; AWorkMode: TWorkMode;
      AWorkCountMax: Int64);
  private
    { Private 宣言 }
    AStream : TFileStream;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses HTTPApp;

{$R *.dfm}

//=============================================================================
//  ダウンロードファイル名をLabelEditに入力
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  Memo1.Clear;
  LabeledEdit1.Text := 'http://mrxray.on.coocan.jp/Delphi/ZipExe/plSetPrinter.zip';
end;

//=============================================================================
//  IdHTTPでダウンロードする
//
//  リダイレクト処理を行っているので,例えばCGI経由のファイルも情報取得可能
//  (ただし少し時間がかかる)
//  htm,html等の場合はソーステキストの取得となるので画像類はタウンロードしない
//
//  SourceFile : ダウンロードするファイル名
//  SaveFile   : ダウンロード後のファイル名
//               TSaveDialogで変更可能
//
//  UnixPathToDosPath関数にはusesにHTTPAppが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
 SourceFile : String;
 SaveFile   : String;
 Ret        : Boolean;
begin
  Memo1.Lines.Clear;
  Panel1.Caption := '';

  SourceFile            := LabeledEdit1.Text;
  ProgressBar1.Position := 0;
  Screen.Cursor         := crHourGlass;

  //リダイレクト処理をTrueに
  IdHTTP1.HandleRedirects := True;

  //ファイルまたはページが見つかったら保存先を選択してダウンロード
  try
    //このURLが存在しないと,Headメソッド実行でエラーとなる
    //リダイレクト実行後に,URLにリダイレクト先のURLが取得できる
    IdHTTP1.Head(SourceFile);
    Ret := True;
  except
    if IdHTTP1.ResponseCode = 302 then begin
      //リダイレクト後のURLで再度Headメソッドを実行して情報取得
      IdHTTP1.Head(IdHTTP1.Response.Location);
      Ret := True;
    end else begin
      Ret := False;
    end;
  end;

  if Ret then begin
    SourceFile := IdHTTP1.URL.URI;
    SaveFile   := ExtractFilePath(ParamStr(0)) +
                  ExtractFileName(UnixPathToDosPath(SourceFile));
    SaveDialog1.FileName := SaveFile;

    if SaveDialog1.Execute then begin
      Update;
      AStream := TFileStream.Create(SaveDialog1.FileName, fmCreate);
      try
        IdHTTP1.Get(SourceFile, AStream);
      finally
        FreeAndNil(AStream);
      end;
      Panel1.Caption := 'ダウンロード終了';
    end;
  end else begin
    MessageDlg('指定したページまたはファイルが見つかりませんでした.',
               mtInformation,
               [mbOK],0);
  end;
  IdHTTP1.Disconnect;

  Screen.Cursor := crDefault;
end;

//=============================================================================
//  IdHTTPのOnWorkBeginイベント
//  プログレスバーの最大値をセット
//=============================================================================
procedure TForm1.IdHTTP1WorkBegin(Sender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Int64);
begin
    ProgressBar1.Max := AWorkCountMax;
end;

//=============================================================================
//  IdHTTPのOnWorkイベント
//  ダウンロードの進捗(進行)状況をプログレスバーに表示
//=============================================================================
procedure TForm1.IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Int64);
var
  StrText : String;
begin
  ProgressBar1.Position := AWorkCount;
  StrText := IntToStr(AStream.Size - AStream.Position);
  Panel1.Caption :='残り ' + StrText + ' バイト';
  Panel1.Update;
end;

//=============================================================================
//  IdHTTPのOnStatusイベント
//  接続やダウンロードの状態をMemoに表示
//=============================================================================
procedure TForm1.IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus;
  const AStatusText: String);
begin
  Memo1.Lines.Add(AStatusText);
end;

end.




05_HTTP サーバからファイルをダウンロード ( スレッドを 使用 )


HTTP サーバからファイルをダウンロードします.IdHTTP コンポーネントの GET メソッドは,ストリームにまとめてダウンロードするため,通常は途中でのキャンセルはできません.
TIdThreadComponent コンポーネントを使用すると,キャンセルの機能を持たせることができます.本サンプルではこのコンポーネントを使用して,[キャンセル] ボタンをクリックしたら,ダウンロードを中止するようにしてみました.


図9
設計時画面
図10
実行画面

リスト5
スレッドを使用してキャンセル機能を追加
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdHTTP, StdCtrls, ExtCtrls, ComCtrls, IdThreadComponent;

type
  TForm1 = class(TForm)
    IdHTTP1: TIdHTTP;
    Panel2: TPanel;
    ProgressBar1: TProgressBar;
    SaveDialog1: TSaveDialog;
    Button1: TButton;
    Button2: TButton;
    LabeledEdit1: TLabeledEdit;
    Panel1: TPanel;
    IdThreadComponent1: TIdThreadComponent;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
      AWorkCount: Int64);
    procedure IdHTTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
      AWorkCountMax: Int64);
    procedure IdThreadComponent1Run(Sender: TIdThreadComponent);
  private
    { Private 宣言 }
    FSaveFileName   : String;
    FSourceFile     : String;
    FSourceFileSize : Int64;
    FIsCancel       : Boolean;
    FDownLoadError  : Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses HTTPApp, IdThread;

{$R *.dfm}

//=============================================================================
//  ダウンロードファイル名をLabelEditに入力
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  LabeledEdit1.Text := 'http://mrxray.on.coocan.jp/Delphi/ZipExe/plSetPrinter.zip';
end;

//=============================================================================
//  フォームを閉じる時の処理
//  接続中の時は接続を切断してスレッドを終了する
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if IdHTTP1.Connected then begin
    IdThreadComponent1.Stop;
    IdHTTP1.Disconnect;
    FIsCancel := True;
  end;
end;

//=============================================================================
//  IdHTTPでダウンロードする
//  リダイレクト処理を行っているので,例えばCGI経由のファイルも情報取得可能
//  (ただし少し時間がかかる)
//  htm,html等の場合はソーステキストの取得となるので画像類はタウンロードしない
//
//  UnixPathToDosPath関数にはusesにHTTPAppが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIsSrcFile : Boolean;
begin
  FSourceFile := LabeledEdit1.Text;
  ProgressBar1.Position := 0;

  //リダイレクト処理をTrueに
  IdHTTP1.HandleRedirects := True;
  try
    //このURLが存在しないとHeadメソッド実行でエラーとなる
    //リダイレクト実行後に,URLにリダイレクト先のURLが取得できる
    IdHTTP1.Head(FSourceFile);
    LIsSrcFile := True;
  except
    if IdHTTP1.ResponseCode = 302 then begin
      //リダイレクト後のURLで再度Headメソッドを実行して情報取得
      IdHTTP1.Head(IdHTTP1.Response.Location);
      LIsSrcFile := True;
    end else begin
      LIsSrcFile := False;
    end;
  end;

  //ダウンロードするファイルが存在する場合
  if LIsSrcFile then begin
    FSourceFile     := IdHTTP1.URL.URI;
    FSaveFileName   := ExtractFilePath(Application.ExeName) +
                       ExtractFileName(UnixPathToDosPath(FSourceFile));
    FSourceFileSize := IdHTTP1.Response.ContentLength;

    SaveDialog1.FileName := FSaveFileName;
    //ローカルの保存先設定
    if SaveDialog1.Execute then begin
      FSaveFileName := SaveDialog1.FileName;

      Screen.Cursor  := crHourGlass;
      FDownLoadError := False;
      //スレッドを開始してダウンロード開始
      IdThreadComponent1.Start;
      //ダウンロードが終了するまで(スレッドが終了するで)待つ
      //OnTerminateやOnStoppedイベントで処理する方法も考えられる
      while True do begin
        if IdThreadComponent1.Terminated then Break;
        Application.ProcessMessages;
        Sleep(1);
      end;

      if FIsCancel then begin
        MessageDlg('ダウンロードを中止しました', mtInformation, [mbOK],0);
      end else
      //ダウンロードに失敗した場合
      if FDownLoadError then begin
        MessageDlg('ダウンロードに失敗しました', mtInformation,[mbOK],0);
      end else begin
        Panel1.Caption := 'ダウンロード終了';
      end;
    end;
  end else begin
    MessageDlg('指定したページまたはファイルが見つかりませんでした',
               mtInformation,
               [mbOK],0);
  end;

  IdHTTP1.Disconnect;
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  キャンセルボタン
//  ダウンロード途中のファイルが存在するので,削除した方がいいかも知れない
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if IdHTTP1.Connected then begin
    FIsCancel := True;
    IdHTTP1.Disconnect;
    IdThreadComponent1.Stop;
  end;
end;

//=============================================================================
//  IdThreadComponent1のOnRunイベント
//  このイベント内でダウンロード処理を行う
//=============================================================================
procedure TForm1.IdThreadComponent1Run(Sender: TIdThreadComponent);
var
  LStream : TFileStream;
begin
  LStream := TFileStream.Create(FSaveFileName, fmCreate);
  try
    try
      //FSourceFileをダウンロードしてLStreamに格納
      IdHTTP1.Get(FSourceFile, LStream);
    except
      //ダウンロード失敗
      FDownLoadError := True;
    end;
  finally
    IdThreadComponent1.Terminate;
    if IdHTTP1.Connected then begin
      IdHTTP1.Disconnect;
    end;
    FreeAndNil(LStream);
  end;
end;

//=============================================================================
//  IdHTTP1のOnWorkBeginイベント処理
//  ダウンロードを開始すると発生するイベント
//=============================================================================
procedure TForm1.IdHTTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Int64);
begin
  ProgressBar1.Max := FSourceFileSize;
end;

//=============================================================================
//  IdHTTP1のOnWorkイベント処理
//  ダウンロード中に発生するイベント
//=============================================================================
procedure TForm1.IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Int64);
var
  LStrText : String;
begin
  ProgressBar1.Position := AWorkCount;

  LStrText := IntToStr(FSourceFileSize - AWorkCount);
  Form1.Panel1.Caption :='残り ' + LStrText + ' バイト';
end;

end.




06_Indy のバージョンを知る方法


Indy のバージョンは,Indy の各コンポーネントの Version プロパティで知ることができます.


図11
設計時画面
図12
実行画面

リスト6
インストールされている Indy のバージョンを知る方法
unit IndyVersionUnit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdHTTP;

type
  TForm1 = class(TForm)
    Button1: TButton;
    IdHTTP1: TIdHTTP;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  Indyのバージョンを調べる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  Application.MessageBox(PChar(IdHTTP1.Version), 'バージョン番号');
end;

end.




07_HTTPS サーバへのアクセス


TIdHTTP を使用して,HTTPS サーバにアクセス (SSL 通信) 手順が下記の記事にあります.
本項の記事とサンプルは,これらの記事を元に作成しています.


OpenSSL の libeay32.dll と ssleay32.dll が必要です.これらの DLL は,Delphi のインストール先の [bin] フォルダ内にありますが,以下のページからも SSL 通信制御関係のファイルとしてダウンロードできます.


今回のテストでは,32 ビットの EXE で使用するので,以下のファイルをダウンロードしました.i386 は 32 ビットの DLL が添付されているファイルです.64 ビットの EXE で使用する場合は,x64 という 64 ビット版の DLL が含まれているファイルをダウンロードします.


  openssl-0.9.8r-i386-win32-rev2.zip  2017-04-02 04:08 831K

ダウンロードしたファイルに含まれている libeay32.dll と ssleay32.dll という 2 つの DLL をプログラムの実行ファイルがあるフォルダにコピーします.Delphi のヘルプには,システム関係フォルダに置くように書いてありますが,システム関係のフォルダ等には置かないようにします.SSL 通信を必要するアプリケーションでは,これらと同名の DLL が使われていて,それらも,そのアプリケーション固有のフォルダ内に置いてあります.
必要であれば openssl.exe もアプリケーションの実行ファイルのフォルダに置きます.openssl.exe は OpenSSL のコマンドラインツールです.


次に,フォームに Indy の TIdSSLIOHandlerSocketOpenSSL コンポーネントを配置します.
そして,TIdHTTP コンポの IOHandler プロパティの値を,この TIdSSLIOHandlerSocketOpenSSL とします.

接続に失敗する場合は, より新しいバージョンの DLL をダウンロードして使用したり,以下の備考のリンクの記事にあるように,TIdSSLIOHandlerSocketOpenSSL コンポーネントの SSLOptins.Method の値を変更してみてください.



[備考 1]
ダウンロードすべき DLL の説明が間違っていたのを 2017年04月03日に修正しました.
2017年04月03日現在の最新のバージョンの DLL が使用可能なのは Indy 10.5.5 以上です.


[備考 2]
FTP での SSL の機能は,Indy 8, Indy 9 では利用できません.


[備考 3]
SSL (Secure Sockets Layer) は,インターネットでデータを暗号化して送受信するための通信手順の一種です.TLS (Transport Layer Security) は SSL の新しい基準ですが,SSL という名前が広く普及しているため SSL/TLS 等と表記することがあります.

[備考 4]
GitHub 等,意味がある User Agent の情報が送られてこないとアクセスを拒否するウェブサイトあるいはウェブページがあります.



図13
IOHandler プロパティの設定
図14
SSLOptins.Method の設定
  • 接続に失敗した場合は sslvSSLv23 にすると接続できることがある
  • デフォルトは sslvTLSv1

以下が実際の実行結果とテストに使用したコードです.
テストに使用しているウェブページは,ファイル名が取得できません.そこで,リダイレクト等の処理は省略しています.タイムアウトの時間も設定していません.保存ファイル名を決め打ちにしています.必要に応じて変更してください.



図15
設計時画面
  • TIdSSLIOHandlerSocketOpenSSL を配置
図16
実行結果
  • AAAA.html というファイル名で保存

リスト7
HTTPS サーバからファイルをダウンロードする簡単な例
ここでは,ウェブページの HTML ファイルをダウンロード
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdHTTP, StdCtrls, ExtCtrls, ComCtrls, IdIOHandler, IdIOHandlerSocket,
  IdIOHandlerStack, IdSSL, IdSSLOpenSSL;

type
  TForm1 = class(TForm)
    IdHTTP1: TIdHTTP;
    Button1: TButton;
    Memo1: TMemo;
    Panel3: TPanel;
    LabeledEdit1: TLabeledEdit;
    Panel1: TPanel;
    IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus;
      const AStatusText: String);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  LabeledEdit1.Text := 'https://code.google.com/p/indydocs/';
end;

//=============================================================================
//  IdHTTPでダウンロードする
//
//  SourceFile : ダウンロードするファイル名
//  SaveFile   : ダウンロードしたファイルの保存ファイル名
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSourceFile : String;
  LSaveFile   : String;
  LStream     : TFileStream;

begin
  Memo1.Lines.Clear;
  Panel1.Caption := '';
  LSourceFile    := LabeledEdit1.Text;
  Screen.Cursor  := crHourGlass;

  //リダイレクト処理をTrueに
  IdHTTP1.HandleRedirects := True;

  LSaveFile := ExtractFilePath(ParamStr(0)) + 'AAA.html';
  LStream   := TFileStream.Create(LSaveFile, fmCreate);
  try
    IdHTTP1.Get(LSourceFile, LStream);
  finally
    FreeAndNil(LStream);
  end;
  Panel1.Caption := 'ダウンロード終了';
  IdHTTP1.Disconnect;

  Screen.Cursor := crDefault;
end;

//=============================================================================
//  IdHTTP1のOnStatusイベント
//  接続等の状態をTMemoに表示
//=============================================================================
procedure TForm1.IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus;
  const AStatusText: String);
begin
  Memo1.Lines.Add(AStatusText);
end;

end.




08_THttpClient または TNetHttpClient を使用する接続処理


Delphi XE8 以降の Delphi であれば,HTTP サーバ,あるいは HTTPS サーバの接続やデータの取得,送信等の操作に THttpClient あるいは TNetHttpClient を使用すると便利です.

Indy は 前項のサンプルのように SSL の機能を独自で処理しますが,THttpClient, TNetHttpClient は OS に実装されている SSL 通信の機能を使用して処理します.
THttpClient のインスタンスは実行時に生成して使用します.TNetHttpClient はコンポーネントパレットの [Net] タブにあります.