Я хотел бы загрузить файл из моей программы Delphi в отдельном потоке, посвященном загрузке.
проблема в том, что основная программа может быть закрыта в любое время (таким образом, поток загрузки может быть также прерван в любое время). Поэтому, даже если нет соединения или сервер зависает на драгоценные секунды, мне нужен способ завершить поток загрузки в секунду или две.
Какую функцию вы, ребята, рекомендуете использовать?
I ' Мы экспериментировали с InterOpenURL / InternetReadFile, но у него нет параметра времени ожидания. У него действительно есть асинхронная версия, но я не смог найти ни одного работающего примера Delphi, и я не уверен, защитит ли меня асинхронная версия от зависаний ...
Использование сокетов было рекомендовано мне ранее, но TClientSocket не ' Похоже, у него также есть функция тайм-аута.
Мне нужно быть полностью подготовленным, поэтому, если у пользователя проблемы с подключением к Интернету на его / ее компьютере или веб-сервер продолжает зависать, мое приложение не зависает перед закрытием.
Имейте в виду, когда отвечаете, что я не хочу использовать ни сторонние компоненты, ни Indy. Любой пример кода с благодарностью.
Спасибо!
TWinSocketStream
имеет таймаут. По сути, вы создаете блокирующий сокет, а затем создаете TWinSocketStream, использующий этот сокет для чтения/записи. (что-то вроде использования StreamWriter). Затем вы выполняете цикл до тех пор, пока соединение не будет закрыто, поток не будет завершен, или пока не будет получено ожидаемое количество байт. Есть функция WaitForData(), которая позволяет вам проверять входящие данные с таймаутом, так что вы никогда не будете "заперты" после этого значения таймаута.
Однако, вам придется самостоятельно обрабатывать процесс http. т.е. посылать 'GET', а затем читать заголовок ответа, чтобы определить, сколько байт ожидать, но это не слишком сложно.
Спасибо GrandmasterB, вот код, который мне удалось собрать: (он находится в блоке Execute моего потока)
var
Buffer: array [0..1024 - 1] of Char;
SocketStream: TWinSocketStream;
RequestString: String;
BytesRead: Integer;
begin
try
ClientSocket.Active := true;
DownloadedData := '';
FillChar(Buffer, SizeOf(Buffer), #0);
if not Terminated then
begin
// Wait 10 seconds
SocketStream := TWinSocketStream.Create(ClientSocket.Socket, 10000);
try
// Send the request to the webserver
RequestString := 'GET /remotedir/remotefile.html HTTP/1.0'#13#10 +
'Host: www.someexamplehost.com'#13#10#13#10;
SocketStream.Write(RequestString[1], Length(RequestString));
// We keep waiting for the data for 5 seconds
if (not Terminated) and SocketStream.WaitForData(5000) then
repeat
// We store the data in our buffer
BytesRead := SocketStream.Read(Buffer, SizeOf(Buffer));
if BytesRead = 0 then
break
else
DownloadedData := DownloadedData + Buffer;
until Terminated or (not SocketStream.WaitForData(2000));
finally
// Close the connection
SocketStream.Free;
if ClientSocket.Active then
ClientSocket.Active := false;
end;
end;
except
end;
Вы должны поместить это в конструктор потока:
ClientSocket := TClientSocket.Create(nil);
ClientSocket.Host := 'www.someexamplehost.com';
ClientSocket.Port := 80;
ClientSocket.ClientType := ctBlocking;
inherited Create(false); // CreateSuspended := false;
А это в деструктор Теги :
ClientSocket.Free;
inherited;
uses ExtActns, ...
type
TfrMain = class(TForm)
...
private
procedure URL_OnDownloadProgress
(Sender: TDownLoadURL;
Progress, ProgressMax: Cardinal;
StatusCode: TURLDownloadStatus;
StatusText: String; var Cancel: Boolean) ;
...
implementation
...
procedure TfrMain.URL_OnDownloadProgress;
begin
ProgressBar1.Max:= ProgressMax;
ProgressBar1.Position:= Progress;
end;
function DoDownload;
begin
with TDownloadURL.Create(self) do
try
URL:='http://0.tqn.com/6/g/delphi/b/index.xml';
FileName := 'c:\ADPHealines.xml';
OnDownloadProgress := URL_OnDownloadProgress;
ExecuteTarget(nil) ;
finally
Free;
end;
end;
{ Заметка: Свойство URL указывает на Интернет Имя_файла — локальный файл }
Это работает.
var
DownloadAbort: boolean;
procedure Download(const URL, FileName: string);
var
hInet: HINTERNET;
hURL: HINTERNET;
Buffer: array[0..1023] of AnsiChar;
BufferLen, amt: cardinal;
f: file;
const
UserAgent = 'Delphi Application'; // Change to whatever you wish
begin
DownloadAbort := false;
hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
try
hURL := InternetOpenUrl(hInet, PChar(URL), nil, 0, 0, 0);
try
FileMode := fmOpenWrite;
AssignFile(f, FileName);
try
Rewrite(f, 1);
repeat
InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);
BlockWrite(f, Buffer[0], BufferLen, amt);
if DownloadAbort then
Exit;
until BufferLen = 0;
finally
CloseFile(f);
end;
finally
InternetCloseHandle(hURL);
end;
finally
InternetCloseHandle(hInet);
end;
end;
На практике приведенный выше код будет частью метода Execute
вашего потока, и вам не нужно DownloadAbort
; вместо этого вы делаете чек
if Terminated then
Exit;