|
| 1 | +unit HttpDownloader; |
| 2 | + |
| 3 | + {$mode objfpc}{$H+} |
| 4 | + |
| 5 | +interface |
| 6 | + |
| 7 | +uses |
| 8 | + ButtonPanel, Classes, ComCtrls, Controls, Dialogs, ExtCtrls, |
| 9 | + Forms, fphttpclient, opensslsockets, StdCtrls, SysUtils; |
| 10 | + |
| 11 | +procedure ShowHttpDownloader(const ACaption, AURL: String; AStream: TStream); |
| 12 | +procedure ShowHttpDownloader(const ACaption, AURL, AFileName: String); |
| 13 | + |
| 14 | +implementation |
| 15 | + |
| 16 | +type |
| 17 | + |
| 18 | + THttpDownloader = class(TForm) |
| 19 | + private |
| 20 | + FPanel: TPanel; |
| 21 | + FProgressBar: TProgressBar; |
| 22 | + FRaiseError: String; |
| 23 | + public |
| 24 | + constructor Create(AOwner: TComponent); override; |
| 25 | + end; |
| 26 | + |
| 27 | +type |
| 28 | + |
| 29 | + THttpDownloaderThread = class(TThread) |
| 30 | + private |
| 31 | + FURL: String; |
| 32 | + FContentLength, FContentPosition: Int64; |
| 33 | + FUserStream: TStream; |
| 34 | + FHttpClient: TFPHTTPClient; |
| 35 | + FHttpDownloader: THttpDownloader; |
| 36 | + public |
| 37 | + constructor Create(AHttpForm: THttpDownloader; const AURL: String; |
| 38 | + AUserStream: TStream); |
| 39 | + protected |
| 40 | + procedure Execute; override; |
| 41 | + procedure SynchronizedClose; |
| 42 | + procedure SynchronizedWrite; |
| 43 | + end; |
| 44 | + |
| 45 | +type |
| 46 | + |
| 47 | + THttpDownloaderStreamProxy = class(TStream) |
| 48 | + strict private |
| 49 | + FThread: THttpDownloaderThread; |
| 50 | + FStream: TStream; |
| 51 | + public |
| 52 | + constructor Create(AThread: THttpDownloaderThread; AStream: TStream); |
| 53 | + public |
| 54 | + function Write(const ABuffer; ACount: Longint): Longint; override; |
| 55 | + end; |
| 56 | + |
| 57 | +constructor THttpDownloaderStreamProxy.Create(AThread: THttpDownloaderThread; |
| 58 | + AStream: TStream); |
| 59 | +begin |
| 60 | + inherited Create; |
| 61 | + FThread := AThread; |
| 62 | + FStream := AStream; |
| 63 | +end; |
| 64 | + |
| 65 | +function THttpDownloaderStreamProxy.Write(const ABuffer; |
| 66 | + ACount: Longint): Longint; |
| 67 | +begin |
| 68 | + Inc(FThread.FContentPosition, ACount); |
| 69 | + if FThread.CheckTerminated then begin |
| 70 | + raise Exception.Create('Download terminated by user'); |
| 71 | + end; |
| 72 | + Result := FStream.Write(ABuffer, ACount); |
| 73 | + FThread.Synchronize(@FThread.SynchronizedWrite); |
| 74 | +end; |
| 75 | + |
| 76 | +constructor THttpDownloaderThread.Create(AHttpForm: THttpDownloader; |
| 77 | + const AURL: String; AUserStream: TStream); |
| 78 | +begin |
| 79 | + FHttpDownloader := AHttpForm; |
| 80 | + FURL := AURL; |
| 81 | + FUserStream := AUserStream; |
| 82 | + inherited Create(False); |
| 83 | +end; |
| 84 | + |
| 85 | +procedure THttpDownloaderThread.SynchronizedClose; |
| 86 | +begin |
| 87 | + FHttpDownloader.Close; |
| 88 | +end; |
| 89 | + |
| 90 | +procedure THttpDownloaderThread.SynchronizedWrite; |
| 91 | +begin |
| 92 | + if FHttpDownloader.FProgressBar.Style = pbstMarquee then begin |
| 93 | + FHttpDownloader.FProgressBar.Style := pbstNormal; |
| 94 | + FContentLength := |
| 95 | + StrToIntDef(FHttpClient.ResponseHeaders.Values['content-length'], 0); |
| 96 | + FHttpDownloader.FProgressBar.Max := FContentLength; |
| 97 | + FHttpDownloader.FProgressBar.Visible := FContentLength > 0; |
| 98 | + end; |
| 99 | + if FHttpDownloader.FProgressBar.Visible then begin |
| 100 | + FHttpDownloader.FPanel.Caption := |
| 101 | + Format('Download @ %d%% (%d of %d bytes)', |
| 102 | + [FContentPosition * 100 div FContentLength, FContentPosition, |
| 103 | + FContentLength]); |
| 104 | + FHttpDownloader.FProgressBar.Position := FContentPosition; |
| 105 | + end else begin |
| 106 | + FHttpDownloader.FPanel.Caption := |
| 107 | + Format('Downloaded %d bytes', [FContentPosition]); |
| 108 | + end; |
| 109 | +end; |
| 110 | + |
| 111 | +procedure THttpDownloaderThread.Execute; |
| 112 | +var |
| 113 | + LStream: TStream; |
| 114 | +begin |
| 115 | + try |
| 116 | + try |
| 117 | + LStream := THttpDownloaderStreamProxy.Create(Self, FUserStream); |
| 118 | + try |
| 119 | + FHttpClient := TFPHTTPClient.Create(nil); |
| 120 | + try |
| 121 | + FHttpClient.AllowRedirect := True; |
| 122 | + FHttpClient.Get(FURL, LStream); |
| 123 | + finally |
| 124 | + FreeAndNil(FHttpClient); |
| 125 | + end; |
| 126 | + finally |
| 127 | + FreeAndNil(LStream); |
| 128 | + end; |
| 129 | + except |
| 130 | + on LException: Exception do begin |
| 131 | + FHttpDownloader.FRaiseError := LException.Message; |
| 132 | + end; |
| 133 | + end; |
| 134 | + finally |
| 135 | + Synchronize(@SynchronizedClose); |
| 136 | + end; |
| 137 | +end; |
| 138 | + |
| 139 | +constructor THttpDownloader.Create(AOwner: TComponent); |
| 140 | +begin |
| 141 | + inherited CreateNew(AOwner); |
| 142 | + Width := 512; |
| 143 | + Height := 128; |
| 144 | + BorderStyle := bsDialog; |
| 145 | + Position := poScreenCenter; |
| 146 | + ChildSizing.TopBottomSpacing := 4; |
| 147 | + ChildSizing.VerticalSpacing := 4; |
| 148 | + ChildSizing.LeftRightSpacing := 4; |
| 149 | + ChildSizing.HorizontalSpacing := 4; |
| 150 | + FProgressBar := TProgressBar.Create(Self); |
| 151 | + FProgressBar.Style := pbstMarquee; |
| 152 | + FProgressBar.Smooth := True; |
| 153 | + FProgressBar.Align := alBottom; |
| 154 | + FProgressBar.Parent := Self; |
| 155 | + with TButtonPanel.Create(Self) do begin |
| 156 | + ShowBevel := False; |
| 157 | + ShowButtons := [pbCancel]; |
| 158 | + BorderSpacing.Around := 0; |
| 159 | + Align := alBottom; |
| 160 | + Parent := Self; |
| 161 | + end; |
| 162 | + FPanel := TPanel.Create(Self); |
| 163 | + FPanel.Caption := 'Waiting for connection'; |
| 164 | + FPanel.BorderStyle := bsSingle; |
| 165 | + FPanel.BevelOuter := bvNone; |
| 166 | + FPanel.Align := alClient; |
| 167 | + FPanel.Parent := Self; |
| 168 | +end; |
| 169 | + |
| 170 | +procedure ShowHttpDownloader(const ACaption, AURL: String; AStream: TStream); |
| 171 | +var |
| 172 | + LHttpForm: THttpDownloader; |
| 173 | +begin |
| 174 | + LHttpForm := THttpDownloader.Create(Application); |
| 175 | + try |
| 176 | + LHttpForm.Caption := ACaption; |
| 177 | + with THttpDownloaderThread.Create(LHttpForm, AURL, AStream) do begin |
| 178 | + try |
| 179 | + LHttpForm.ShowModal; |
| 180 | + Terminate; |
| 181 | + WaitFor; |
| 182 | + finally |
| 183 | + Free; |
| 184 | + end; |
| 185 | + end; |
| 186 | + if LHttpForm.FRaiseError <> EmptyStr then begin |
| 187 | + raise Exception.Create(LHttpForm.FRaiseError); |
| 188 | + end; |
| 189 | + finally |
| 190 | + FreeAndNil(LHttpForm); |
| 191 | + end; |
| 192 | +end; |
| 193 | + |
| 194 | +procedure ShowHttpDownloader(const ACaption, AURL, AFileName: String); |
| 195 | +var |
| 196 | + LStream: TStream; |
| 197 | +begin |
| 198 | + LStream := TFileStream.Create(AFileName, fmCreate); |
| 199 | + try |
| 200 | + ShowHttpDownloader(ACaption, AURL, LStream); |
| 201 | + finally |
| 202 | + FreeAndNil(LStream); |
| 203 | + end; |
| 204 | +end; |
| 205 | + |
| 206 | +end. |
0 commit comments