下載大文件時,斷點(diǎn)續(xù)傳是很有必要的,特別是網(wǎng)速度慢且不穩(wěn)定的情況下,很難保證不出意外,一旦意外中斷,又要從頭下載,會很讓人抓狂。斷點(diǎn)續(xù)傳就能很好解決意外中斷情況,再次下載時不需要從頭下載,從上次中斷處繼續(xù)下載即可,這樣下載幾G或十幾G大小的一個文件都沒問題。本文介紹利用miniframe開源Web框架分別在lazarus、delphi下實(shí)現(xiàn)文件HTTP下載斷點(diǎn)續(xù)傳的功能。
本文Demo還實(shí)現(xiàn)了批量下載文件,同步服務(wù)器上的文件到客戶端的功能。文件斷點(diǎn)續(xù)傳原理:分塊下載,下載后客戶端逐一合并,同時保存已下載的位置,當(dāng)意外中斷再次下載時從保存的位置開始下載即可。這其中還要保證,中斷后再次下載時服務(wù)器上相應(yīng)的文件如果更新了,還得重新下載,不然下載到的文件是錯了。說明:以下代碼lazarus或delphi環(huán)境下都能使用。全部源碼及Demo請到miniframe開源web框架下載:?https://www.wyeditor.com/miniframe/或https://github.com/dajingshan/miniframe。
服務(wù)器端代碼
文件下載斷點(diǎn)續(xù)傳服務(wù)器端很簡單,只要提供客戶端要求下載的開始位置和指定大小的塊即可。
以下是服務(wù)器獲取文件信息和下載一個文件一塊的代碼:
- <%@//Script頭、過程和函數(shù)定義
- program codes;
- %>
- ?
- <%!//聲明變量
- var
- i,lp: integer;
- FileName, RelativePath, FromPath, ErrStr: string;
- json: TminiJson;
- FS: TFileStream;
- function GetOneDirFileInfo(Json: TminiJson; Path: string): string;
- var
- Status: Integer;
- SearchRec: TSearchRec;
- json_sub: TminiJson;
- begin
- Path := PathWithSlash(Path);
- SearchRec := TSearchRec.Create;
- Status := FindFirst(Path + '*.*', faAnyFile, SearchRec);
- try
- while Status = 0 do
- begin
- if SearchRec.Attr and faDirectory = faDirectory then
- begin
- if (SearchRec.name <> '.') and (SearchRec.name <> '..') then
- GetOneDirFileInfo(Json, Path + SearchRec.Name + '\');
- end else
- begin
- FileName := Path + SearchRec.Name;
- try
- if FileExists(FileName) then
- begin
- json_sub := Pub.GetJson;
- json_sub.SO; //初始化 或 json.Init;
- json_sub.S['filename'] := SearchRec.name;
- json_sub.S['RelativePath'] := GetDeliBack(FileName, FromPath);
- json_sub.S['FileTime'] := FileGetFileTimeA(FileName);
- json_sub.I['size'] := SearchRec.Size;
- json.A['list'] := json_sub;
- end;
- except
- //print(ExceptionParam)
- end;//}
- end;
- Status := FindNext(SearchRec);
- end;
- finally
- FindClose(SearchRec);
- SearchRec.Free;
- end;//*)
- end;
- %>
- <%
- begin
- FromPath := 'D:\code\delphi\sign\發(fā)行文件'; //下載源目錄
- json := Pub.GetJson; //這樣創(chuàng)建json對象不需要自己釋放,系統(tǒng)自動管理
- json.SO; //初始化 或 json.Init;
- // 驗(yàn)證是否登錄代碼
- {if not Request.IsLogin('Logined') then
- begin
- json.S['retcode'] := '300';
- json.S['retmsg'] := '你還沒有登錄(no logined)!';
- print(json.AsJson(true));
- exit;
- end;//}
- json.S['retcode'] := '200';
- json.S['retmsg'] := '成功!';
- if Request.V('opr') = '1' then
- begin //獲取服務(wù)上指定目錄的文件信息
- GetOneDirFileInfo(Json, FromPath);
- end else
- if Request.V('opr') = '2' then
- begin //下載指定文件給定大小的塊
- FromPath := PathWithSlash(FromPath);
- RelativePath := Request.V('fn');
- FileName := FromPath + RelativePath;
- Fs := Pub.GetFS(FileName, fmShareDenyWrite, ErrStr);
- if trim(ErrStr) <> '' then
- begin
- json.S['retcode'] := '300';
- json.S['retmsg'] := ErrStr;
- print(json.AsJson(true));
- exit;
- end;
- Fs.Position := StrToInt(Request.V('pos'));
- Response.ContentStream := TMemoryStream.Create; //注意不能用 Pub.GetMs,這是因?yàn)镻ub.GetMs創(chuàng)建的對象在動態(tài)腳本運(yùn)行完就釋放了
- Response.ContentStream.CopyFrom(Fs, StrToInt(Request.V('size')));
- //返回流數(shù)據(jù)
- Response.ContentType := 'application/octet-stream';
- end;
- print(json.AsJson(true));
- end;
- %>
客戶端代碼
客戶端收到塊后,進(jìn)行合并。全部塊下載完成后,還要把新下載的文件的文件修改為與服務(wù)器上的文件相同。以下是客戶端實(shí)現(xiàn)的主代碼:文章來源:http://www.zghlxwxcb.cn/news/detail-642106.html
- procedure TMainForm.UpgradeBlock_Run(var ThreadRetInfo: TThreadRetInfo);
- const
- BlockSize = 1024*1024; //1M
- var
- HTML, ToPath, RelativePath, FN, Tmp, TmpFileName, FailFiles, SuccFiles, Newfn, TmpToPath: string;
- Json, TmpJson: TminiJson;
- lp, I, Number, HadUpSize, AllSize, AllBlockCount, MySize, MyNumber: Int64;
- Flag: boolean;
- SL, SLDate, SLSize, SLTmp: TStringlist;
- MS: TMemoryStream;
- Fs: TFileStream;
- procedure HintMsg(Msg: string);
- begin
- FMyMsg := Msg; // '正在獲取文件列表。。。';
- ThreadRetInfo.Self.Synchronize(ThreadRetInfo.Self, MyUpdateface); //為什么不直接用匿名,因?yàn)閘az不支持
- end;
- begin
- ToPath := 'D:\superhtml'; //如果是當(dāng)前程序更新 ExtractFilePath(ParamStr(0))
- ?
- ThreadRetInfo.Ok := false;
- ?
- HintMsg('正在獲取文件列表。。。');
- if not HttpPost('/接口/同步文件到客戶端.html?opr=1',
- '', ThreadRetInfo.ErrStr, ThreadRetInfo.HTML) then exit;
- if Pos('{', ThreadRetInfo.HTML) <> 1 then
- begin
- ThreadRetInfo.ErrStr :='請先檢查腳本源碼是否配置正確!';
- exit;
- end;
- ToPath := Pub.PathWithSlash(ToPath);
- ?
- Json := TminiJson.Create;
- SL := TStringlist.Create;
- SLDate := TStringlist.Create;
- SLSize := TStringlist.Create;
- SLTmp := TStringlist.Create;
- try
- Json.LoadFromString(ThreadRetInfo.HTML);
- if json.S['retcode'] = '200' then
- begin
- TmpJson := json.A['list'];
- for lp := 0 to TmpJson.length - 1 do
- begin
- HintMsg(lp.ToString + '/' + TmpJson.length.ToString + '正在檢查文件:' + RelativePath);
- RelativePath := TmpJson[lp].S['RelativePath'];
- if trim(RelativePath) = '' then Continue;
- Flag := FileExists(ToPath + RelativePath);
- if Flag then
- begin
- if (PubFile.FileGetFileTimeA(ToPath + RelativePath) = TmpJson[lp].S['FileTime']) and
- (PubFile.FileGetFileSize(ToPath + RelativePath) = TmpJson[lp].I['Size']) then
- else
- Flag := false;
- end;
- if not Flag then //此文件需要更新
- begin
- SL.Add(RelativePath);
- SLDate.Add(TmpJson[lp].S['FileTime']);
- SLSize.Add(TmpJson[lp].S['Size']);
- end;
- end;
- ?
- //開始下載
- FailFiles := '';
- SuccFiles := '';
- HintMsg('需要更新的文件共有' + IntToStr(SL.Count) + '個。。。');
- for lp := 0 to SL.Count - 1 do
- begin
- RelativePath := SL[lp];
- if RelativePath[1] = '\' then RelativePath := Copy(RelativePath, 2, MaxInt);
- FN := ToPath + RelativePath;
- ?
- //先計(jì)算要分幾個包,以處理進(jìn)度
- Number := 0;
- HadUpSize := 0;
- AllSize := StrToInt64(SLSize[lp]);
- AllBlockCount := 0;
- while true do
- begin
- AllBlockCount := AllBlockCount + 1;
- if AllSize - HadUpSize >= BlockSize then
- MySize := BlockSize
- else
- MySize := AllSize - HadUpSize;
- HadUpSize := HadUpSize + MySize;
- if HadUpSize >= AllSize then
- break;
- end;
- ?
- //開始分塊下載
- Number := 0;
- HadUpSize := 0;
- //AllSize := Fs.Size;
- //TmpToPath := PubFile.FileGetTemporaryPath;
- Newfn := '@_' + PubPWD.GetMd5(SLDate[lp] + SLSize[lp]) + ExtractFileName(FN); //Pub.GetClientUniqueCode;
- ?
- if FileExists(ToPath + Newfn) and (FileExists(FN)) then
- begin
- SLTmp.LoadFromFile(ToPath + Newfn);
- MyNumber := StrToInt64(trim(SLTmp.Text));
- Fs := TFileStream.Create(FN, fmOpenWrite);
- end else
- begin
- MyNumber := 0;
- Fs := TFileStream.Create(FN, fmCreate);
- end;
- try
- while true do
- begin
- HintMsg('正在下載文件[' + Pub.GetDeliBack(RelativePath, '@@') + ']第[' + IntToStr(Number + 1) + '/' + IntToStr(AllBlockCount) + ']個包。。。');
- ?
- if AllSize - HadUpSize >= BlockSize then
- MySize := BlockSize
- else
- MySize := AllSize - HadUpSize;
- Number := Number + 1;
- if (MyNumber = 0) or (Number >= MyNumber) or (HadUpSize + MySize >= AllSize) then
- begin
- for I := 1 to 2 do //意外出錯重試一次
- begin
- if not HttpPost('/接口/同步文件到客戶端.html?opr=2fn=' + UrlEncode(RelativePath) +
- 'pos=' + UrlEncode(IntToStr(HadUpSize)) + 'size=' + UrlEncode(IntToStr(MySize)),
- '', ThreadRetInfo.ErrStr, ThreadRetInfo.HTML, MS) then
- begin
- if I = 2 then
- begin
- ThreadRetInfo.ErrStr := Json.S['retmsg'];
- exit;
- end else
- Continue;
- end;
- if Pos('{', ThreadRetInfo.HTML) < 1 then
- begin
- if I = 2 then
- begin
- ThreadRetInfo.ErrStr := Json.S['retmsg'];
- exit;
- end else
- Continue;
- end;
- ?
- Json.LoadFromString(ThreadRetInfo.HTML);
- if json.S['retcode'] <> '200' then
- begin
- if I = 2 then
- begin
- ThreadRetInfo.ErrStr := Json.S['retmsg'];
- exit;
- end else
- Continue;
- end;
- break;
- end;
- ?
- if MS = nil then
- begin
- ThreadRetInfo.ErrStr := '沒能下載到文件[' + RelativePath + ']!' + json.S['retmsg'];
- exit;
- end else
- begin
- Fs.Position := HadUpSize;
- MS.Position := 0;
- Fs.CopyFrom(MS, MS.Size);
- MS.Free;
- MS := nil;
- SLTmp.Text := Number.ToString;
- try
- SLTmp.SaveToFile(ToPath + Newfn);
- except
- end;
- end;
- end;
- HadUpSize := HadUpSize + MySize;
- ?
- if HadUpSize >= AllSize then
- begin //全部下載完成
- Fs.Free;
- Fs := nil;
- Sleep(10);
- PubFile.FileChangeFileDate(Fn, SLDate[lp]);
- DeleteFile(ToPath + Newfn);
- SuccFiles := SuccFiles + #13#10 + RelativePath;
- break;
- end;
- end;
- finally
- if Fs <> nil then
- Fs.Free;
- end;
- end;
- ThreadRetInfo.HTML := '';
- if trim(SuccFiles) <> '' then
- ThreadRetInfo.HTML := '本次更新了以下文件:'#13#10 + SuccFiles;
- //if trim(FailFiles) <> '' then
- //ThreadRetInfo.HTML := trim(ThreadRetInfo.HTML + #13#10'以下文件更新失?。?span id="n5n3t3z" class="str">'#13#10 + FailFiles);
- end;
- finally
- SLTmp.Free;
- SLSize.Free;
- SL.Free;
- Json.Free;
- SLDate.Free;
- end;
- ThreadRetInfo.Ok := true;
- end;
- ?
以下是Demo運(yùn)行界面:文章來源地址http://www.zghlxwxcb.cn/news/detail-642106.html
到了這里,關(guān)于lazarus、delphi文件Http下載斷點(diǎn)續(xù)傳的實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!