2010-08-23 1 views
7

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

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

Какую функцию вы особенно рекомендуете?

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

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

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

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

Спасибо!

+0

Параметр 'InternetReadFile' можно назвать с произвольным количеством байтов, которые будут извлекаться в цикле. Если вы объедините это с завершенной проверкой в ​​своем потоке, отметьте поток для завершения при закрытии приложения и дождитесь окончания потока, у вас не должно быть никаких «зависаний». –

ответ

2

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

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

+0

Есть ли у вас примерный код? :-) – Steve

+1

Увы, не лично. Обычно я использую библиотеку синапсов для такого рода вещей. Я нашел это онлайн - это выглядит правильно, но YMMV. http://delphi.xcjc.net/viewthread.php?tid=29580 В этом примере используется сокет, но принцип будет одинаков - ждать данных, читать данные, выходить, если он истечет. – GrandmasterB

5

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

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; Intead, вы проверяете

if Terminated then 
    Exit; 
+0

Привет! Приведенный выше код не будет работать, если InternetReadFile зависает. Прежде чем отправлять свой вопрос, я просматривал часами и читал сообщения об этом. – Steve

+0

@Steve: Вы когда-нибудь наблюдали «InternetReadFile»? Обычно вы можете доверять Windows API. В конце концов, он используется каждый день миллионами разработчиков. –

+0

Нетрудно найти информацию об этом, например: http://www.codeguru.com/forum/archive/index.php/t-355462.html Но если вы думаете об этом, являются многочисленными частями, где это может пойти не так: проблема с подключением к Интернету пользователя, нерабочий прокси, проблема с Wi-Fi-маршрутизатором, медленный отклик от интернет-сервера ... Я думаю, что даже InternetOpenURL мог бы повесить. Так что мне нужны функции, которые не ждут ответа, но сразу же вернут управление и при необходимости отмените отмену. Любые идеи? – Steve

2

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

Это выглядит хорошо! Ответ будет содержать заголовки HTTP-ответов, поэтому полученный вами файл будет фактически запускаться после двух последовательных # D # A, которые отделяют заголовок от данных. Вы можете закрыть сокет, как только вы получите количество байтов, указанное в заголовке ответа, - это предотвратит зависание последних 5 секунд, пока он ждет, будет ли еще больше данных. – GrandmasterB

1

от 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 является локальным файлом }

Смежные вопросы