如何使用Indy实现支持断点续传的文件下载?
解决Indy IdHTTP断点续传失效问题
核心逻辑
断点续传的关键是告诉服务器从已下载的位置继续传输,同时本地文件以追加模式写入,而不是覆盖。你之前调用IdHttp1.Disconnect()只是断开了连接,但恢复时没有向服务器传递续传位置的信息,导致重新从头下载。
具体实现步骤
1. 记录已下载字节数
暂停下载时,先获取本地已下载文件的大小(如果是边下载边写入,就是当前文件的实际字节数),将这个值保存到变量中,比如FDownloadedSize: Int64。
2. 恢复时设置Range请求头
重新发起下载请求前,给IdHTTP1.Request.Range赋值,格式为bytes=已下载大小-。例如已下载了5120字节,就设置为'bytes=5120-',告诉服务器从第5121字节开始传输。
3. 本地文件以追加模式打开
恢复下载时,不要创建新文件,而是用fmOpenWrite or fmShareDenyWrite or fmAppend模式打开本地文件,确保新下载的内容追加到已有内容末尾。
完整示例代码
unit DownloadDemo; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdIOHandlerSocket, IdIOHandlerStack, IdSSLOpenSSL; type TMainForm = class(TForm) IdHTTP1: TIdHTTP; btnStartResume: TButton; btnPause: TButton; edtFileURL: TEdit; edtSavePath: TEdit; procedure btnStartResumeClick(Sender: TObject); procedure btnPauseClick(Sender: TObject); private FDownloadStream: TFileStream; FDownloadedSize: Int64; FIsPaused: Boolean; procedure ExecuteDownload; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnStartResumeClick(Sender: TObject); begin if FIsPaused then begin // 恢复下载 ExecuteDownload; btnStartResume.Caption := '开始下载'; FIsPaused := False; end else begin // 首次下载或重置下载 FDownloadedSize := 0; if FileExists(edtSavePath.Text) then begin // 读取已有文件大小作为续传起点 var TempStream := TFileStream.Create(edtSavePath.Text, fmOpenRead or fmShareDenyWrite); FDownloadedSize := TempStream.Size; TempStream.Free; end; ExecuteDownload; end; btnPause.Enabled := True; end; procedure TMainForm.btnPauseClick(Sender: TObject); begin // 确保文件流数据写入磁盘 if Assigned(FDownloadStream) then FDownloadStream.Position := FDownloadStream.Size; // 断开连接暂停下载 IdHTTP1.Disconnect; FreeAndNil(FDownloadStream); FIsPaused := True; btnStartResume.Caption := '恢复下载'; btnPause.Enabled := False; end; procedure TMainForm.ExecuteDownload; begin try if FDownloadedSize > 0 then begin // 设置续传请求头 IdHTTP1.Request.Range := Format('bytes=%d-', [FDownloadedSize]); // 追加模式打开文件 FDownloadStream := TFileStream.Create(edtSavePath.Text, fmOpenWrite or fmShareDenyWrite or fmAppend); end else begin // 首次下载,创建新文件 FDownloadStream := TFileStream.Create(edtSavePath.Text, fmCreate or fmShareDenyWrite); end; // 执行下载 IdHTTP1.Get(edtFileURL.Text, FDownloadStream); ShowMessage('下载完成'); btnPause.Enabled := False; except on E: Exception do begin if E is EIdConnClosedGracefully then begin // 主动断开引发的异常,标记为暂停状态 FIsPaused := True; btnStartResume.Caption := '恢复下载'; end else ShowMessage('下载出错:' + E.Message); end; end; end; end.
关键注意事项
- 提前验证服务器支持:可以先发送
HEAD请求,检查响应头的Accept-Ranges字段是否为bytes,确认服务器支持断点续传。 - 下载过程中不要随意修改文件流位置,避免数据写入异常。
- 服务器返回
206 Partial Content响应码时,说明续传成功;如果返回200 OK,则表示服务器不支持续传,会重新下载整个文件。
内容的提问来源于stack exchange,提问作者user10825637
相关产品推荐
相关产品推荐

