高速ファイルのアップロードとダウンロードを処理するためのDelphiのコードとアイデア

高速ファイルのアップロードとダウンロードを処理するための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

気に入った場合は、下の[いいね]と[お気に入り]をクリックするだけで、次の共有を見ることができます。

 

おすすめ

転載: blog.csdn.net/pulledup/article/details/108660481