lazarus、delphi文件Http下载断点续传的实现

这篇具有很好参考价值的文章主要介绍了lazarus、delphi文件Http下载断点续传的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

下载大文件时,断点续传是很有必要的,特别是网速度慢且不稳定的情况下,很难保证不出意外,一旦意外中断,又要从头下载,会很让人抓狂。断点续传就能很好解决意外中断情况,再次下载时不需要从头下载,从上次中断处继续下载即可,这样下载几G或十几G大小的一个文件都没问题。本文介绍利用miniframe开源Web框架分别在lazarus、delphi下实现文件HTTP下载断点续传的功能。

本文Demo还实现了批量下载文件,同步服务器上的文件到客户端的功能。文件断点续传原理:分块下载,下载后客户端逐一合并,同时保存已下载的位置,当意外中断再次下载时从保存的位置开始下载即可。这其中还要保证,中断后再次下载时服务器上相应的文件如果更新了,还得重新下载,不然下载到的文件是错了。说明:以下代码lazarus或delphi环境下都能使用。全部源码及Demo请到miniframe开源web框架下载: https://www.wyeditor.com/miniframe/或https://github.com/dajingshan/miniframe。

服务器端代码

文件下载断点续传服务器端很简单,只要提供客户端要求下载的开始位置和指定大小的块即可。

以下是服务器获取文件信息和下载一个文件一块的代码:

  1. <%@//Script头、过程和函数定义
  2. program codes;
  3. %>
  4.  
  5. <%!//声明变量
  6. var
  7. i,lp: integer;
  8. FileName, RelativePath, FromPath, ErrStr: string;
  9. json: TminiJson;
  10. FS: TFileStream;
  11. function GetOneDirFileInfo(Json: TminiJson; Path: string): string;
  12. var
  13. Status: Integer;
  14. SearchRec: TSearchRec;
  15. json_sub: TminiJson;
  16. begin
  17. Path := PathWithSlash(Path);
  18. SearchRec := TSearchRec.Create;
  19. Status := FindFirst(Path + '*.*', faAnyFile, SearchRec);
  20. try
  21. while Status = 0 do
  22. begin
  23. if SearchRec.Attr and faDirectory = faDirectory then
  24. begin
  25. if (SearchRec.name <> '.') and (SearchRec.name <> '..') then
  26. GetOneDirFileInfo(Json, Path + SearchRec.Name + '\');
  27. end else
  28. begin
  29. FileName := Path + SearchRec.Name;
  30. try
  31. if FileExists(FileName) then
  32. begin
  33. json_sub := Pub.GetJson;
  34. json_sub.SO; //初始化 或 json.Init;
  35. json_sub.S['filename'] := SearchRec.name;
  36. json_sub.S['RelativePath'] := GetDeliBack(FileName, FromPath);
  37. json_sub.S['FileTime'] := FileGetFileTimeA(FileName);
  38. json_sub.I['size'] := SearchRec.Size;
  39. json.A['list'] := json_sub;
  40. end;
  41. except
  42. //print(ExceptionParam)
  43. end;//}
  44. end;
  45. Status := FindNext(SearchRec);
  46. end;
  47. finally
  48. FindClose(SearchRec);
  49. SearchRec.Free;
  50. end;//*)
  51. end;
  52. %>
  53. <%
  54. begin
  55. FromPath := 'D:\code\delphi\sign\发行文件'; //下载源目录
  56. json := Pub.GetJson; //这样创建json对象不需要自己释放,系统自动管理
  57. json.SO; //初始化 或 json.Init;
  58. // 验证是否登录代码
  59. {if not Request.IsLogin('Logined') then
  60. begin
  61. json.S['retcode'] := '300';
  62. json.S['retmsg'] := '你还没有登录(no logined)!';
  63. print(json.AsJson(true));
  64. exit;
  65. end;//}
  66. json.S['retcode'] := '200';
  67. json.S['retmsg'] := '成功!';
  68. if Request.V('opr') = '1' then
  69. begin //获取服务上指定目录的文件信息
  70. GetOneDirFileInfo(Json, FromPath);
  71. end else
  72. if Request.V('opr') = '2' then
  73. begin //下载指定文件给定大小的块
  74. FromPath := PathWithSlash(FromPath);
  75. RelativePath := Request.V('fn');
  76. FileName := FromPath + RelativePath;
  77. Fs := Pub.GetFS(FileName, fmShareDenyWrite, ErrStr);
  78. if trim(ErrStr) <> '' then
  79. begin
  80. json.S['retcode'] := '300';
  81. json.S['retmsg'] := ErrStr;
  82. print(json.AsJson(true));
  83. exit;
  84. end;
  85. Fs.Position := StrToInt(Request.V('pos'));
  86. Response.ContentStream := TMemoryStream.Create; //注意不能用 Pub.GetMs,这是因为Pub.GetMs创建的对象在动态脚本运行完就释放了
  87. Response.ContentStream.CopyFrom(Fs, StrToInt(Request.V('size')));
  88. //返回流数据
  89. Response.ContentType := 'application/octet-stream';
  90. end;
  91. print(json.AsJson(true));
  92. end;
  93. %>

客户端代码

客户端收到块后,进行合并。全部块下载完成后,还要把新下载的文件的文件修改为与服务器上的文件相同。以下是客户端实现的主代码:

  1. procedure TMainForm.UpgradeBlock_Run(var ThreadRetInfo: TThreadRetInfo);
  2. const
  3. BlockSize = 1024*1024; //1M
  4. var
  5. HTML, ToPath, RelativePath, FN, Tmp, TmpFileName, FailFiles, SuccFiles, Newfn, TmpToPath: string;
  6. Json, TmpJson: TminiJson;
  7. lp, I, Number, HadUpSize, AllSize, AllBlockCount, MySize, MyNumber: Int64;
  8. Flag: boolean;
  9. SL, SLDate, SLSize, SLTmp: TStringlist;
  10. MS: TMemoryStream;
  11. Fs: TFileStream;
  12. procedure HintMsg(Msg: string);
  13. begin
  14. FMyMsg := Msg; // '正在获取文件列表。。。';
  15. ThreadRetInfo.Self.Synchronize(ThreadRetInfo.Self, MyUpdateface); //为什么不直接用匿名,因为laz不支持
  16. end;
  17. begin
  18. ToPath := 'D:\superhtml'; //如果是当前程序更新 ExtractFilePath(ParamStr(0))
  19.  
  20. ThreadRetInfo.Ok := false;
  21.  
  22. HintMsg('正在获取文件列表。。。');
  23. if not HttpPost('/接口/同步文件到客户端.html?opr=1',
  24. '', ThreadRetInfo.ErrStr, ThreadRetInfo.HTML) then exit;
  25. if Pos('{', ThreadRetInfo.HTML) <> 1 then
  26. begin
  27. ThreadRetInfo.ErrStr :='请先检查脚本源码是否配置正确!';
  28. exit;
  29. end;
  30. ToPath := Pub.PathWithSlash(ToPath);
  31.  
  32. Json := TminiJson.Create;
  33. SL := TStringlist.Create;
  34. SLDate := TStringlist.Create;
  35. SLSize := TStringlist.Create;
  36. SLTmp := TStringlist.Create;
  37. try
  38. Json.LoadFromString(ThreadRetInfo.HTML);
  39. if json.S['retcode'] = '200' then
  40. begin
  41. TmpJson := json.A['list'];
  42. for lp := 0 to TmpJson.length - 1 do
  43. begin
  44. HintMsg(lp.ToString + '/' + TmpJson.length.ToString + '正在检查文件:' + RelativePath);
  45. RelativePath := TmpJson[lp].S['RelativePath'];
  46. if trim(RelativePath) = '' then Continue;
  47. Flag := FileExists(ToPath + RelativePath);
  48. if Flag then
  49. begin
  50. if (PubFile.FileGetFileTimeA(ToPath + RelativePath) = TmpJson[lp].S['FileTime']) and
  51. (PubFile.FileGetFileSize(ToPath + RelativePath) = TmpJson[lp].I['Size']) then
  52. else
  53. Flag := false;
  54. end;
  55. if not Flag then //此文件需要更新
  56. begin
  57. SL.Add(RelativePath);
  58. SLDate.Add(TmpJson[lp].S['FileTime']);
  59. SLSize.Add(TmpJson[lp].S['Size']);
  60. end;
  61. end;
  62.  
  63. //开始下载
  64. FailFiles := '';
  65. SuccFiles := '';
  66. HintMsg('需要更新的文件共有' + IntToStr(SL.Count) + '个。。。');
  67. for lp := 0 to SL.Count - 1 do
  68. begin
  69. RelativePath := SL[lp];
  70. if RelativePath[1] = '\' then RelativePath := Copy(RelativePath, 2, MaxInt);
  71. FN := ToPath + RelativePath;
  72.  
  73. //先计算要分几个包,以处理进度
  74. Number := 0;
  75. HadUpSize := 0;
  76. AllSize := StrToInt64(SLSize[lp]);
  77. AllBlockCount := 0;
  78. while true do
  79. begin
  80. AllBlockCount := AllBlockCount + 1;
  81. if AllSize - HadUpSize >= BlockSize then
  82. MySize := BlockSize
  83. else
  84. MySize := AllSize - HadUpSize;
  85. HadUpSize := HadUpSize + MySize;
  86. if HadUpSize >= AllSize then
  87. break;
  88. end;
  89.  
  90. //开始分块下载
  91. Number := 0;
  92. HadUpSize := 0;
  93. //AllSize := Fs.Size;
  94. //TmpToPath := PubFile.FileGetTemporaryPath;
  95. Newfn := '@_' + PubPWD.GetMd5(SLDate[lp] + SLSize[lp]) + ExtractFileName(FN); //Pub.GetClientUniqueCode;
  96.  
  97. if FileExists(ToPath + Newfn) and (FileExists(FN)) then
  98. begin
  99. SLTmp.LoadFromFile(ToPath + Newfn);
  100. MyNumber := StrToInt64(trim(SLTmp.Text));
  101. Fs := TFileStream.Create(FN, fmOpenWrite);
  102. end else
  103. begin
  104. MyNumber := 0;
  105. Fs := TFileStream.Create(FN, fmCreate);
  106. end;
  107. try
  108. while true do
  109. begin
  110. HintMsg('正在下载文件[' + Pub.GetDeliBack(RelativePath, '@@') + ']第[' + IntToStr(Number + 1) + '/' + IntToStr(AllBlockCount) + ']个包。。。');
  111.  
  112. if AllSize - HadUpSize >= BlockSize then
  113. MySize := BlockSize
  114. else
  115. MySize := AllSize - HadUpSize;
  116. Number := Number + 1;
  117. if (MyNumber = 0) or (Number >= MyNumber) or (HadUpSize + MySize >= AllSize) then
  118. begin
  119. for I := 1 to 2 do //意外出错重试一次
  120. begin
  121. if not HttpPost('/接口/同步文件到客户端.html?opr=2fn=' + UrlEncode(RelativePath) +
  122. 'pos=' + UrlEncode(IntToStr(HadUpSize)) + 'size=' + UrlEncode(IntToStr(MySize)),
  123. '', ThreadRetInfo.ErrStr, ThreadRetInfo.HTML, MS) then
  124. begin
  125. if I = 2 then
  126. begin
  127. ThreadRetInfo.ErrStr := Json.S['retmsg'];
  128. exit;
  129. end else
  130. Continue;
  131. end;
  132. if Pos('{', ThreadRetInfo.HTML) < 1 then
  133. begin
  134. if I = 2 then
  135. begin
  136. ThreadRetInfo.ErrStr := Json.S['retmsg'];
  137. exit;
  138. end else
  139. Continue;
  140. end;
  141.  
  142. Json.LoadFromString(ThreadRetInfo.HTML);
  143. if json.S['retcode'] <> '200' then
  144. begin
  145. if I = 2 then
  146. begin
  147. ThreadRetInfo.ErrStr := Json.S['retmsg'];
  148. exit;
  149. end else
  150. Continue;
  151. end;
  152. break;
  153. end;
  154.  
  155. if MS = nil then
  156. begin
  157. ThreadRetInfo.ErrStr := '没能下载到文件[' + RelativePath + ']!' + json.S['retmsg'];
  158. exit;
  159. end else
  160. begin
  161. Fs.Position := HadUpSize;
  162. MS.Position := 0;
  163. Fs.CopyFrom(MS, MS.Size);
  164. MS.Free;
  165. MS := nil;
  166. SLTmp.Text := Number.ToString;
  167. try
  168. SLTmp.SaveToFile(ToPath + Newfn);
  169. except
  170. end;
  171. end;
  172. end;
  173. HadUpSize := HadUpSize + MySize;
  174.  
  175. if HadUpSize >= AllSize then
  176. begin //全部下载完成
  177. Fs.Free;
  178. Fs := nil;
  179. Sleep(10);
  180. PubFile.FileChangeFileDate(Fn, SLDate[lp]);
  181. DeleteFile(ToPath + Newfn);
  182. SuccFiles := SuccFiles + #13#10 + RelativePath;
  183. break;
  184. end;
  185. end;
  186. finally
  187. if Fs <> nil then
  188. Fs.Free;
  189. end;
  190. end;
  191. ThreadRetInfo.HTML := '';
  192. if trim(SuccFiles) <> '' then
  193. ThreadRetInfo.HTML := '本次更新了以下文件:'#13#10 + SuccFiles;
  194. //if trim(FailFiles) <> '' then
  195. //ThreadRetInfo.HTML := trim(ThreadRetInfo.HTML + #13#10'以下文件更新失败:'#13#10 + FailFiles);
  196. end;
  197. finally
  198. SLTmp.Free;
  199. SLSize.Free;
  200. SL.Free;
  201. Json.Free;
  202. SLDate.Free;
  203. end;
  204. ThreadRetInfo.Ok := true;
  205. end;
  206.  

以下是Demo运行界面:文章来源地址https://www.toymoban.com/news/detail-642106.html

金蜘蛛网页设计器
©2020-2023版权所有
 

到了这里,关于lazarus、delphi文件Http下载断点续传的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring-Boot实现HTTP大文件断点续传分片下载-大视频分段渐进式播放

    服务端如何将一个大视频文件做切分,分段响应给客户端,让浏览器可以渐进式地播放。 Spring Boot实现HTTP分片下载断点续传,从而实现H5页面的大视频播放问题,实现渐进式播放,每次只播放需要播放的内容就可以了,不需要加载整个文件到内存中。 文件的断点续传、文件多

    2024年02月11日
    浏览(56)
  • 基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传

    1. 前言 文件上传 小文件(图片、文档、视频)上传可以直接使用很多ui框架封装的上传组件,或者自己写一个input 上传,利用FormData 对象提交文件数据,后端使用spring提供的MultipartFile进行文件的接收,然后写入即可。但是对于比较大的文件,比如上传2G左右的文件(http上传

    2024年02月06日
    浏览(48)
  • 【前端面试】中大文件上传/下载:中等文件代理服务器放行+大文件切片传输+并发请求+localstorage实现断点续传

    目录 切片上传~spark-md5 原理:流式计算+分块处理 文件标识spark-md5:A-B A.切片哈希值合并 B.首尾切片+其他切片前中后各取2M 计算hash:A-B(参考React的Fiber架构) A.线程:web-worker B.空闲:requestIdleCallback 异步并发控制:A-B(参考http2的多路复用) A.promise.allSettled() B.并发数max=

    2024年02月12日
    浏览(56)
  • 断点续传下载引出的http header的range和content-range参数

    最近同事在做安卓的断点续传下载,然后遇到了在请求头添加RANGE参数设置时: 网络上找的资料都是设置contentLength,同时测试后,发现中间下载无法返回206成功,最终发现是需要end参数-1.此处稍微记录一下,主要了解一下相关的知识。 HTTP1.1 协议(RFC2616)开始支持获取文件的

    2024年02月10日
    浏览(65)
  • 断点续传下载:深入理解 HTTP Header 中的 Range 和 Content-Range 参数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAhEY9hc-1687309020087)(https://example.com/resume-download-image)] 在进行文件下载时,我们经常会遇到网络不稳定或其他中断情况。为了提供更好的用户体验和节省带宽资源,断点续传技术应运而生。HTTP 协议通过

    2024年02月10日
    浏览(52)
  • Java实现文件断点续传

    文件断点续传代码 测试代码

    2024年03月09日
    浏览(41)
  • 旧版Xcode文件较大导致下载总是失败但又不能断点续传重新开始的解决方法

    旧版mac下载旧版Xcode时需要进入https://developer.apple.com/download/all/?q=xcode下载,但是下载这些文件需要登录。登录后下载中途很容易失败,失败后又必须重新下载。 下载这里面的内容都需要登录,经过分析需提供其cookie,我这里使用的是motrix。需要下载时先通过搜索找到你要下载

    2024年02月15日
    浏览(71)
  • Minio大文件分片上传、断点续传实现

    使用minio api实现分片上传及断点续传功能。 前端准备:获取大文件的MD5值,将文件分片,5M为一分片,排好顺序,并按顺序命名(1,2,3这种后面比较好合并) 在上传分片阶段,前端有上传进度条 1、检验文件MD5值 1.1 redis中查看MD5是否存在 1.2 判断临时文件夹是否存在 boolean d

    2024年02月09日
    浏览(53)
  • 大文件切片上传+断点续传解决方案-前后端实现(附源码)

    上传文件大家应该都做过,前端直接把file文件传给后端就ok了,但是大文件这样传就会造成页面假死,体验极差。如果遇到网络不稳定的时候,中途上传失败的话,又要从头开始传,本来文件就大,还慢。所以今天我们用一种新方法-切片上传+断点续传 页面上很简单,我就放

    2024年02月09日
    浏览(47)
  • 【SpringBoot整合系列】SpringBoot 实现大文件分片上传、断点续传及秒传

    小文件(图片、文档、视频)上传可以直接使用很多ui框架封装的上传组件,或者自己写一个input 上传,利用FormData 对象提交文件数据,后端使用spring提供的MultipartFile进行文件的接收,然后写入即可。 但是对于比较大的文件,比如上传2G左右的文件(http上传),就需要将文件

    2024年04月16日
    浏览(67)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包