2010-08-23 41 views
7

Tôi muốn tải xuống tệp từ chương trình Delphi của tôi trong một chuỗi riêng biệt dành riêng cho việc tải xuống.Tải xuống tệp từ Internet trong khi có thể hủy tải xuống bất kỳ lúc nào

Sự cố là chương trình chính có thể bị đóng bất kỳ lúc nào (do đó, chuỗi tải xuống cũng có thể bị chấm dứt bất kỳ lúc nào). Vì vậy, ngay cả khi không có kết nối hoặc máy chủ bị trễ trong giây quý giá, tôi cần một cách để chấm dứt chuỗi tải xuống trong vòng một hoặc hai giây.

Bạn khuyến nghị sử dụng chức năng nào?

Tôi đã thử nghiệm với InterOpenURL/InternetReadFile nhưng không có tham số hết giờ. Nó có một phiên bản không đồng bộ nhưng tôi không thể tìm thấy bất kỳ ví dụ Delphi làm việc và tôi không chắc chắn nếu phiên bản không đồng bộ sẽ bảo vệ tôi khỏi bị treo ...

Sử dụng ổ cắm đã được đề xuất cho tôi trước đây, nhưng TClientSocket doesn dường như không có chức năng hết thời gian.

Tôi cần được chuẩn bị đầy đủ để nếu người dùng có sự cố kết nối Internet trên máy tính của mình hoặc máy chủ web tiếp tục trì hoãn ứng dụng của tôi không bị treo trước khi đóng.

Hãy ghi nhớ khi trả lời rằng tôi không muốn sử dụng bất kỳ thành phần của bên thứ ba cũng như Indy. Bất kỳ mã ví dụ nào đều được đánh giá cao.

Cảm ơn bạn!

+0

Lệnh 'InternetReadFile' có thể được gọi với một số tiền tùy ý các byte được lấy trong một vòng lặp. Nếu bạn kết hợp điều này với một kiểm tra chấm dứt trong chủ đề của bạn, gắn cờ cho các chủ đề chấm dứt khi đóng ứng dụng và chờ cho các chủ đề để chấm dứt, bạn không nên có bất kỳ "treo". –

Trả lời

2

TWinSocketStream có thời gian chờ. Về cơ bản bạn tạo một socket chặn, và sau đó tạo một TWinSocketStream bằng cách sử dụng socket đó để đọc/ghi. (giống như sử dụng StreamWriter). Sau đó, bạn lặp lại cho đến khi kết nối được đóng lại, luồng được chấm dứt hoặc bạn đạt đến số byte dự kiến. Có một WaitForData() cho phép bạn kiểm tra dữ liệu đến w/một thời gian chờ, vì vậy bạn không bao giờ 'bị khóa' vượt quá giá trị thời gian chờ đó.

Bạn sẽ phải tự xử lý quy trình http. ví dụ, gửi 'GET', và sau đó đọc tiêu đề phản hồi để xác định có bao nhiêu byte mong đợi, nhưng điều đó không quá khó.

+0

Bạn có một số mã ví dụ không? :-) – Steve

+1

Than ôi, không phải cá nhân. Tôi thường sử dụng thư viện synapse cho loại điều này. Tôi đã tìm thấy trực tuyến này - có vẻ đúng, nhưng YMMV. http://delphi.xcjc.net/viewthread.php?tid=29580 Ví dụ này sử dụng một socketthread, nhưng nguyên tắc sẽ giống nhau - đợi dữ liệu, đọc dữ liệu, thoát ra nếu nó hết thời gian. – GrandmasterB

5

Tác phẩm này.

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; 

Trong thực tế, các mã trên sẽ là một phần của phương pháp của chủ đề của bạn Execute, và bạn không cần phải DownloadAbort; intead, bạn kiểm tra

if Terminated then 
    Exit; 
+0

Xin chào! Đoạn mã trên sẽ không hoạt động nếu InternetReadFile bị treo. Trước khi đăng câu hỏi của tôi, tôi đã duyệt hàng giờ và đọc báo cáo về điều này. – Steve

+0

@Steve: Có * bạn * đã từng quan sát thấy 'InternetReadFile' bị treo? Thông thường, bạn có thể tin tưởng API Windows. Sau khi tất cả, nó được sử dụng hàng ngày bởi hàng triệu nhà phát triển. –

+0

Không khó để tìm thông tin về nó, hãy xem thông tin này, ví dụ: http://www.codeguru.com/forum/archive/index.php/t-355462.html Nhưng nếu bạn nghĩ về nó, có là nhiều phần mà nó có thể đi sai: vấn đề với kết nối Internet của người dùng, một proxy không hoạt động, vấn đề với các bộ định tuyến wifi, phản ứng chậm từ máy chủ Internet ... Tôi nghĩ rằng ngay cả InternetOpenURL có thể tiếp tục treo. Vì vậy, tôi cần các chức năng sẽ không chờ phản hồi, nhưng hãy trả lại ngay lập tức và cho phép hủy nếu cần. Bất kỳ ý tưởng nào? – Steve

2

Nhờ GrandmasterB, đây là đoạn code tôi đã quản lý để đặt lại với nhau: (đó là trong khối chủ đề của tôi 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; 

Bạn phải đặt này để các nhà xây dựng của các chủ đề:

ClientSocket := TClientSocket.Create(nil); 
    ClientSocket.Host := 'www.someexamplehost.com'; 
    ClientSocket.Port := 80; 
    ClientSocket.ClientType := ctBlocking; 

    inherited Create(false); // CreateSuspended := false; 

Và đây đến desctructor:

ClientSocket.Free; 
    inherited; 
+0

Điều đó có vẻ tốt! Phản hồi sẽ có tiêu đề phản hồi http trong đó, vì vậy tệp bạn nhận được sẽ thực sự bắt đầu sau hai lần # D # A liên tiếp, tách biệt tiêu đề khỏi dữ liệu. Bạn có thể đóng socket ngay khi bạn nhận được số byte được chỉ định trong tiêu đề phản hồi - điều này sẽ ngăn 5 giây cuối cùng bị treo trong khi chờ đợi xem có thêm dữ liệu nào đang đến hay không. – GrandmasterB

1

Từ 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; 

{ Lưu ý: điểm bất động sản URL với Internet FileName là file địa phương }

Các vấn đề liên quan