高速ファイルのアップロードとダウンロードを処理するためのDelphiのコードとアイデア
アップロードとダウンロードは、方向が異なる2つの概念です。以下の対応するクライアントとサーバーのコード:方向転換、ダウンロードを意味します。もう一度方向転換、アップロードを意味します。
1.アイデア
1.セクションごとに大きなファイルをアップロードまたはダウンロードします(つまり、「ブレークポイント」のアップロードまたはダウンロード)
2.これらのセグメントを個別にアップロードまたはダウンロードします
3.アップロードまたはダウンロード後に、「セグメント化された」ファイルストリームを結合して復元します
4.加速について(インターネット上で「高速」アップロードまたはダウンロードがよく見られます):複数のスレッドがこれらのセグメントを別々にアップロードまたはダウンロードする場合、速度を上げることができます。
Gao Yongのコードを使用しますが、まったく問題ありません。これは、高速化ソリューションであるGao Yongのコードを提供するためのものであり、このアイデアに従って少し変更することで、高速でアップロードまたはダウンロードできます。
二、コード
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;
begin
if (Path.Trim='')
or (ServerPath.Trim='')
or (ServerFileName.Trim='') then
begin
AResultStr:='文件不能为空!';
Result := AResultStr ;
exit;
end;
if FileExists(Path) =true then
begin
AResultStr:='文件存在!';
//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 do
begin //文件流无需SetSize:
//ATStream:将大文件流AWriteStream拆分成:
//三位数的小临时文件.tmp的文件流:
ACopyStream:= TMemoryStream.Create;
ATStream:=TFileStream.Create( Path
+FillBeforeString(IntToStr(FileBufferCount),10,'0')+'.tmp'
,fmCreate or fmOpenReadWrite);//:FillBeforeString:自写函数,字符串末尾向左补齐字符
//:文件流,没有就产生、有就打开 //10位:足够1T的文件拆分
try
if FileSize < AWriteStream.Size then FileSize:= BufSize;
if FileSize >= AWriteStream.Size then
FileSize:=AWriteStream.Size - (iCircle-1) * BufSize;
ReadCount := AWriteStream.Read( Buffer[0], FileSize );
AResultStr:=AResultStr+','+IntToStr(FileSize);
if ReadCount > 0 then
ATStream.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);//:需要的场景才这样做
except
AWriteStream.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 then
begin
try
ifResult:=ServerMethods
.mergeFilesUseTFileStream(ServerPath + ServerFileName);
//while ifResult <>true do sleep(0);//:服务器方法成功执行需要时间,需要的场景才做
except
AWriteStream.disposeOf; ATStream.disposeOf; ACopyStream.disposeOf;
Result := '文件流合并错误'; exit;
end;
AResultStr:= '文件上传成功';
AWriteStream.disposeOf;
end;
end else
begin
//小文件:已知小于最小设定值,则直接复制数据:
ACopyStream:= TMemoryStream.Create;
ACopyStream.CopyFrom(AWriteStream,0);
try
ifResult:=ServerMethods
.upLoadFileFromTStreamBufferRead( ACopyStream,(ServerPath+ServerFileName) );
//:服务器方法成功执行需要时间
//while ifResult<>true do sleep(0);//:需要的场景才这样做
AResultStr:=IntToStr(AWriteStream.Size);
except
AWriteStream.disposeOf; ACopyStream.disposeOf;
Result := '服务器出错了'; exit;
end;
if ifResult=true then
begin
AResultStr:= '文件上传成功';
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;
begin
if FileExists(toFilePath)=true then
begin
TThread.Synchronize(nil,
procedure
begin
TFile.Delete(toFilePath);
MainServerForm.Memo_Errors.Lines.Add('删除文件:'+toFilePath.Trim);
end
);
end;//:存在就先删除
//删除后然后产生新文件:
FS := TFileStream.Create(toFilePath, System.Classes.fmCreate);
//:常量默认fmCreate = $FF00; //: TFileStream create mode
TThread.Synchronize(nil,
procedure
begin//测试:
//MainServerForm.Memo_Errors.Lines.Add('新产生文件:'+toFilePath.Trim);
end
);
try
//if ATStream.Size = -1 then//:大小未知则一直读取到没有数据为止:
begin
SetLength( Buffer, BufSize );
repeat
ReadCount := ATStream.Read( Buffer[0], BufSize );
if ReadCount > 0 then
FS.WriteBuffer( Buffer[0], ReadCount );
if ReadCount < BufSize then
break;
until ReadCount < BufSize;
end //else FS.CopyFrom(ATStream, 0)
; //:大小已知则直接复制数据
finally
FS.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;
begin
TDirectory.SetCurrentDirectory(System.IOUtils.TPath.GetLibraryPath);
fileName:=System.IOUtils.TPath.GetLibraryPath + toFilePath;
if TDirectory.Exists(TDirectory.GetParent(fileName)) then
begin
currPath:=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);//:搜索成功返回0
if filesCount<>0 then
begin
System.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 do
begin
filesName.Add(SearchRec.Name);
tmpTStream:=TFileStream.Create(SearchRec.Name,fmOpenRead);
try
//Vcl.Forms.Application.ProcessMessages;
//:不卡界面:VCL将线程池TServerMethods1的消息事件交给应用的主线程
//:不卡界面:FMX用线程同步方法TThread.Synchronize
FileTStream.CopyFrom(tmpTStream,0);
filesCount := FindNext(SearchRec);
finally
tmpTStream.disposeOf;
end;
end;
try
for filesCount := 0 to filesName.Count-1 do
begin
if FileExists(filesName[filesCount].Trim)=true then
TFile.Delete(filesName[filesCount].Trim);
end;
finally
System.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
気に入った場合は、下の[いいね]と[お気に入り]をクリックするだけで、次の共有を見ることができます。