Загрузка файла из Интернета с возможностью прервать загрузку в любое время - PullRequest
7 голосов
/ 23 августа 2010

Я бы хотел загрузить файл из моей программы Delphi в отдельном потоке, посвященном загрузке.

Проблема в том, что основная программа может быть закрыта в любое время (таким образом, поток загрузки может быть прерванв любое время).Поэтому, даже если соединение отсутствует или сервер зависает на драгоценные секунды, мне нужен способ прекратить поток загрузки в течение одной или двух секунд.

Какую функцию вы, ребята, рекомендуете использовать?

Я экспериментировал с InterOpenURL / InternetReadFile, но у него нет параметра времени ожидания.У него действительно есть асинхронная версия, но я не смог найти ни одного работающего примера Delphi, и я не уверен, защитит ли меня асинхронная версия от зависаний ...

Использование сокетов было рекомендовано мне ранее, ноTClientSocket, похоже, не имеет функции тайм-аута.

Мне нужно быть полностью подготовленным, поэтому, если у пользователя проблемы с подключением к Интернету на его / ее компьютере или веб-сервер продолжает отставать, мое приложение неt повесить перед закрытием.

Пожалуйста, имейте в виду, отвечая, что я не хочу использовать ни сторонние компоненты, ни Indy.Любой пример кода с благодарностью.

Спасибо!

Ответы [ 4 ]

5 голосов
/ 23 августа 2010

Это работает.

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;
2 голосов
/ 24 августа 2010

Благодаря 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;

И это для desctructor:

  ClientSocket.Free;
  inherited;
2 голосов
/ 23 августа 2010

TWinSocketStream имеет тайм-аут. Вы в основном создаете блокирующий сокет, а затем создаете TWinSocketStream, используя этот сокет для чтения / записи. (вроде как использование StreamWriter). Затем вы выполняете цикл до тех пор, пока соединение не будет закрыто, поток не будет прерван, или вы не достигнете ожидаемого количества байтов. Есть функция WaitForData (), которая позволяет вам проверять входящие данные без тайм-аута, поэтому вы никогда не будете «заблокированы» после этого тайм-аута.

Однако вам придется самостоятельно обрабатывать http-процесс. то есть отправьте «GET», а затем прочитайте заголовок ответа, чтобы определить, сколько байтов ожидать, но это не так уж сложно.

1 голос
/ 25 августа 2010

С delphi.about.com


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 указывает на Интернет FileName - это локальный файл }

...