Delphi处理高速文件上传下载的代码及思路

news/2024/12/21 22:20:50/

Delphi处理高速文件上传下载的代码及思路

      上传和下载是一对方向不同的概念,下面对应的客户端和服务器代码:掉个头,它就是下载;再掉个头,它就是上传。

一、思路

      1、将大文件:分段(即常说的“断点”上传或下载)上传或下载

      2、分别上传或下载这些分段

      3、将上传或下载后的各“分段”文件流合并还原

      4、关于加速(网上你经常看到“高速”上传或下载):若多线程分别上传或下载这些分段,即可提速。

      你们去使用 高勇老师的代码,它是完全无问题;这里仅仅给大家提供一种提速的方案,高勇老师的代码,你按照这个思路稍作改装,即可高速上传或下载。

二、代码

      2.1、客户端: 

  ///<summary>RestFull上传文件函数(文件流方法):///上传单个文件(TFileStream实例化抽象流:文件分段传参给服务器):///</summary>
function CopyFileTFileStream(const Path: string;const BufSize :Integer= (1*1024*1024); //:BufSize大小默认值1MB:为1个分段const ServerPath:string='api\upload\';const ServerFileName:string='A1.pdf' ):string;  
var Buffer: TBytes; ReadCount,FileSize,FileBufferCount,iCircle: Integer;ATStream,AWriteStream: TFileStream; ACopyStream:TStream;AResultStr:string; ifResult: Boolean;
beginif (Path.Trim='') or (ServerPath.Trim='') or (ServerFileName.Trim='') thenbeginAResultStr:='文件不能为空!';Result := AResultStr ;  exit;  end;if FileExists(Path) =true thenbeginAResultStr:='文件存在!';//AWriteStream:读本地大文件的流:AWriteStream := TFileStream.Create( Path,fmOpenRead );iCircle:=ceil( AWriteStream.Size /BufSize );//:分几段读取,就写几个临时文件.tmp://大文件:已知大于最小设定值,则分段读取,直到读完为止:if AWriteStream.Size > BufSize then//:不能用FileSize显式的判断begin //:需要分数据包处理,如果是服务端是Datasnap默认32k的缓冲区(缓存,太JB小啦)://文件流无需SetSize,而是设置其Buffer:TBytes字节数组的长度://SetLength(Buffer, BufSize);SetLength( Buffer,(5*1024*1024) ); //:如果设置的较大:超内存需要控制,单独写例程//$F000;//$F000:61440:60kb//:System.Classes.fmCreate//:印象中好像最大缓冲区就这个大小,你自己核实吧,5兆我够了FileBufferCount:=0;  FileSize:=BufSize;AResultStr:=''; for FileBufferCount := 1 to iCircle dobegin  //文件流无需SetSize://ATStream:将大文件流AWriteStream拆分成://三位数的小临时文件.tmp的文件流:ACopyStream:= TMemoryStream.Create;ATStream:=TFileStream.Create( Path+FillBeforeString(IntToStr(FileBufferCount),10,'0')+'.tmp',fmCreate or fmOpenReadWrite);//:FillBeforeString:自写函数,字符串末尾向左补齐字符//:文件流,没有就产生、有就打开 //10位:足够1T的文件拆分tryif FileSize < AWriteStream.Size then FileSize:= BufSize;if FileSize >= AWriteStream.Size thenFileSize:=AWriteStream.Size - (iCircle-1) * BufSize;ReadCount := AWriteStream.Read( Buffer[0], FileSize );AResultStr:=AResultStr+','+IntToStr(FileSize); if ReadCount > 0 thenATStream.WriteBuffer(Buffer[0], ReadCount);ACopyStream.CopyFrom(ATStream,0);//:ACopyStream:由CopyFrom的调用者自己来释放try ifResult:=ServerMethods.upLoadFileFromTStreamBufferRead(ACopyStream,( ServerPath +ServerFileName+FillBeforeString(IntToStr(FileBufferCount),3,'0')+'.tmp') );//:服务器方法成功执行需要时间://while ifResult<>true do sleep(0);//:需要的场景才这样做exceptAWriteStream.disposeOf; ATStream.disposeOf; ACopyStream.disposeOf; Result := '流加载错误'; exit;end;  FileSize:=FileSize + BufSize;finally//传完该临时分段文件流释放它的句柄,并删除它对应的临时文件:ATStream.disposeOf; System.IOUtils.TFile.Delete(Path+FillBeforeString(IntToStr(FileBufferCount),3,'0')+'.tmp');end;end;//确保:全部临时分段文件流处理完成之后,发出合并文件请求,//将分段文件流还原为原始文件:if FileBufferCount =iCircle thenbegin try ifResult:=ServerMethods.mergeFilesUseTFileStream(ServerPath + ServerFileName);//while ifResult <>true do sleep(0);//:服务器方法成功执行需要时间,需要的场景才做exceptAWriteStream.disposeOf; ATStream.disposeOf; ACopyStream.disposeOf; Result := '文件流合并错误'; exit;end;AResultStr:=  '文件上传成功';AWriteStream.disposeOf;end;end elsebegin//小文件:已知小于最小设定值,则直接复制数据:ACopyStream:= TMemoryStream.Create;ACopyStream.CopyFrom(AWriteStream,0);try  ifResult:=ServerMethods.upLoadFileFromTStreamBufferRead( ACopyStream,(ServerPath+ServerFileName) );//:服务器方法成功执行需要时间//while ifResult<>true do sleep(0);//:需要的场景才这样做AResultStr:=IntToStr(AWriteStream.Size);exceptAWriteStream.disposeOf; ACopyStream.disposeOf; Result := '服务器出错了'; exit;end;if ifResult=true thenbeginAResultStr:= '文件上传成功';AWriteStream.disposeOf; end;end;end else AResultStr:='文件不存在!';System.IOUtils.TDirectory.SetCurrentDirectory(System.IOUtils.TPath.GetLibraryPath);Result := AResultStr ;
end;

 

2.2、服务端: 

/// <summary>将(大文件拆分后的)单个小临时文件上传到服务器的函数;///参数为文件数据流和服务器端应用库路径下的子路径 </summary>
function TServerMethods1.upLoadFileFromTStreamBufferRead(const ATStream:TStream; const toFilePath:string): Boolean;
const BufSize = $F000; //:默认$F000:61440:60kb的小文件 //System.Classes.fmCreate
var Buffer: TBytes; ReadCount,times: Integer; FS: TFileStream;
beginif FileExists(toFilePath)=true thenbeginTThread.Synchronize(nil,procedurebeginTFile.Delete(toFilePath);MainServerForm.Memo_Errors.Lines.Add('删除文件:'+toFilePath.Trim);end);end;//:存在就先删除//删除后然后产生新文件:FS := TFileStream.Create(toFilePath, System.Classes.fmCreate); //:常量默认fmCreate = $FF00; //: TFileStream create modeTThread.Synchronize(nil,procedurebegin//测试://MainServerForm.Memo_Errors.Lines.Add('新产生文件:'+toFilePath.Trim);end);try//if ATStream.Size = -1 then//:大小未知则一直读取到没有数据为止:beginSetLength( Buffer, BufSize );repeatReadCount := ATStream.Read( Buffer[0], BufSize );if ReadCount > 0 thenFS.WriteBuffer( Buffer[0], ReadCount );if ReadCount < BufSize thenbreak;until ReadCount < BufSize;end //else FS.CopyFrom(ATStream, 0); //:大小已知则直接复制数据finallyFS.Free;Result := True;end;
end;/// <summary>多文件流合并(用文件流方法实例化抽象流):</summary>
function TServerMethods1.mergeFilesUseTFileStream(const toFilePath:string): Boolean;
var filesCount :Integer;  SearchRec :TSearchRec;  currPath :string;fileName :string;  filesName :TStringList; FileTStream,tmpTStream :TStream;
beginTDirectory.SetCurrentDirectory(System.IOUtils.TPath.GetLibraryPath);fileName:=System.IOUtils.TPath.GetLibraryPath + toFilePath;if TDirectory.Exists(TDirectory.GetParent(fileName)) thenbegincurrPath:=TDirectory.GetParent(fileName);//强制路径产生://ForceDirectories(currPath) //DirectoryExists:路径是否存在//FileExists:文件是否存在:uses System.SysUtils;TDirectory.SetCurrentDirectory(currPath);//:进入该路径(设置当前路径并进入)if FileExists(fileName)=true then TFile.Delete(fileName);end;filesCount := FindFirst(currPath + '\' + '*.tmp', faAnyFile, SearchRec);//:搜索成功返回0if filesCount<>0 thenbeginSystem.SysUtils.FindClose(SearchRec);//?不识别:需要带上前缀:System.SysUtils.TDirectory.SetCurrentDirectory(System.IOUtils.TPath.GetLibraryPath);Result :=false; exit;end;     FileTStream:=TFileStream.Create(fileName,fmCreate);filesName:=TStringList.Create;while filesCount = 0 dobeginfilesName.Add(SearchRec.Name);tmpTStream:=TFileStream.Create(SearchRec.Name,fmOpenRead);try//Vcl.Forms.Application.ProcessMessages;//:不卡界面:VCL将线程池TServerMethods1的消息事件交给应用的主线程//:不卡界面:FMX用线程同步方法TThread.SynchronizeFileTStream.CopyFrom(tmpTStream,0);filesCount := FindNext(SearchRec);finallytmpTStream.disposeOf;  end;end;tryfor filesCount := 0 to filesName.Count-1 dobeginif FileExists(filesName[filesCount].Trim)=true thenTFile.Delete(filesName[filesCount].Trim);end;finallySystem.SysUtils.FindClose(SearchRec);//?不识别:需要带上前缀:System.SysUtils.filesName.disposeOf;  FileTStream.disposeOf; TDirectory.SetCurrentDirectory(System.IOUtils.TPath.GetLibraryPath);end;Result :=true;
end;

 

       并行上传,CPU 8路全开,几乎全利用率满负荷上传。

 

本博客关联文章:源码下载:https://download.csdn.net/download/pulledup/12578989

《TTreeView完整的枚举和递归算法》 https://blog.csdn.net/pulledup/article/details/103687816

喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:

 


http://www.ppmy.cn/news/701130.html

相关文章

昂**诚供应链管理系统任意文件上传漏洞复现 CNVD-2023-26756

目录 1.漏洞概述 2.影响版本 3.漏洞等级 4.漏洞复现 5.Nuclei自动化扫描POC 5.修复建议

511遇见易语言资源表的导入和导出

易语言资源表添加声音资源&#xff0c;图片资源&#xff0c;图片组资源&#xff0c;和向资源表中导入可执行文件&#xff0c;将资源表中的资源导出&#xff0c;使用资源表中的资源&#xff0c;通过随机播放音乐&#xff0c;随机播放图片&#xff0c;已经把exe文件写出并执行做了…

百度OCR识别表格文字,并自动下载到本地(准确率很高)

一、输入文件及申请的Token import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.List; import java.util.Map; import j…

免费调用快递鸟物流跟踪轨迹订阅接口技术文档

物流跟踪由轨迹订阅接口和轨迹推送接口组成&#xff0c;对接时需要对接以下二个接口 1.轨迹订阅接口 1.1 功能说明 快递鸟物流轨迹订阅接口用于向快递鸟订阅物流轨迹信息。将订单内容通过订阅接口订阅到快递鸟&#xff0c;客户可自动获取运单的轨迹节点信息。 免费试用接口…

神策(Android)- 在曝光采集基础上学习项目架构

开篇的时候我就在想这篇blog到底有没有意义&#xff1f;因为本身使用的就是神策提供的功能&#xff0c;同时神策也提供了很完善的文档&#xff0c;而唯一要我们做的也仅仅是将它正确的集成到项目内&#xff0c;并且随着版本升级&#xff0c;文档肯定也会有一定变更… 不过&…

Flask boostrap实现图片视频上传下载展示

Flask boostrap实现图片视频上传下载展示 1、展示效果2、前端代码3、后端代码 1、展示效果 项目目录结构 2、前端代码 html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title>&l…

在Blender和Zbrush中创建激光指示器,新手硬表面建模码住!

大家好&#xff0c;今天云渲染小编给大家带来的分享是硬表面建模&#xff0c;CG艺术家Lyubov使用Blender和Zbrush创建激光指示器的幕后花絮。 介绍 我叫 Lyubov&#xff0c;来自俄罗斯圣彼得堡&#xff0c;是一名 3D 建模的初学者。虽然学习还不到一年&#xff0c;但是我对它…

解决 fatal: Authentication failed for ‘https://github.com/*/*.git/‘

原因&#xff1a;github 的认证策略发生了改变&#xff0c;在 2021年8月13日 的时候&#xff0c;用户名加密码的认证方式被去掉了&#xff0c;换成了 个人令牌&#xff08;Personal Access Token&#xff09;的校验方式。 官网解决方案&#xff1a;管理个人访问令牌 - GitHub …