unit uSendFileInChunks_TPS_ChunkUploader;

interface

uses

  System.SysUtils, System.Classes,System.NetEncoding, System.Types,
  System.JSON, uCreateUniqueName_TPS_ChunkUploader,System.IOUtils,
  Vcl.Dialogs,

  //myUnits

  uDirectories_TPS_ChunkUploader,
  uDivideAndGatherFile_TPS_ChunkUploader
  ;



type
TUploadInChunksEvent = procedure (Sender: TObject; ACountBytes:Int64)  of object;


type
  TSendFileInChunks_TPS_ChunkUploader = class(TDataModule)
  private




    FDivideAndGatherFile:TDivideAndGatherFile_TPS_ChunkUploader;

    FRelativeOutputDirOnServer:string;
    FRelativeTempDirOnServer:string;
    FAbsoluteTempDirOnClient:string;








    FOnDivideInChunksOnClientBegin:TUploadInChunksEvent;
    {< field for Event FOnDivideInChunksOnClient Begin}

    FOnDivideInChunksOnClient:TUploadInChunksEvent;
    {< field for Event FOnDivideInChunksOnClient}

    FOnDivideInChunksOnClientEnd:TUploadInChunksEvent;
    {< field for Event FOnDivideInChunksOnClient Begin}



    FOnUploadInChunksBegin:TUploadInChunksEvent;
    {< field for Event OnUploadInChunksBegin}

    FOnUploadInChunks:TUploadInChunksEvent;
    {< field for Event FOnUploadInChunks}


    FOnGatherChunksOnServerBegin:TUploadInChunksEvent;
    {< field for Event FOnSendingChunksToServer}

    FOnUploadInChunksEnd:TUploadInChunksEvent;
    {< field for Events FOnUploadInChunksEnd}


    { Private declarations }
  public
    { Public declarations }

  constructor Create(AOwner: TComponent); override;
  destructor Destroy(); override;

  function GetFileSize(AFilePath: String): Int64; // additional here, used in other modules

  function SendFileInChunks(AFilePath:String; AChunkSize:Int64):Boolean;
  {< MAIN FUNCTION IN THIS }
  function SendFileInChunksSafeFromInterruptions(AFilePath:String; AChunkSize:Int64):Boolean;
  {< // ATTENTION PLEASE - THIS FUNCTION NOT FINISHED FOR THE MOMENT}

  function SendOneWholeFile(AFilePath: String; ARelativeDirOnServer:String):Boolean;
  function GatherInfoAboutChunksToFile(AChunksDir,AChunksInfoFileName:string;AFileSize:int64):Boolean;
  function SendChunksInfoFile(ATempDirOnServer, AChunksInfoFilePath:string):Boolean; // Deprecated

  property DivideAndGatherFile:TDivideAndGatherFile_TPS_ChunkUploader read
  FDivideAndGatherFile write FDivideAndGatherFile;


  //property RelativeOutputDirOnServer:string read FRelativeOutputDirOnServer write FRelativeOutputDirOnServer;
  //property RelativeTempDirOnServer:string read FRelativeTempDirOnServer write FRelativeTempDirOnServer;
  //property AbsoluteTempDirOnClient:string read FAbsoluteTempDirOnClient write FAbsoluteTempDirOnClient;




  //Events properties

  property OnDivideInChunksOnClientBegin:TUploadInChunksEvent read FOnDivideInChunksOnClientBegin write FOnDivideInChunksOnClientBegin;
  property OnDivideInChunksOnClient:TUploadInChunksEvent read FOnDivideInChunksOnClient write FOnDivideInChunksOnClient;
  property OnDivideInChunksOnClientEnd:TUploadInChunksEvent read FOnDivideInChunksOnClientEnd write FOnDivideInChunksOnClientEnd;

  property OnUploadInChunksBegin:TUploadInChunksEvent read FOnUploadInChunksBegin write FOnUploadInChunksBegin;
  property OnUploadInChunks:TUploadInChunksEvent read FOnUploadInChunks write FOnUploadInChunks;
  property OnUploadInChunksEnd:TUploadInChunksEvent read FOnUploadInChunksEnd write FOnUploadInChunksEnd;

  property OnGatherChunksOnServer:TUploadInChunksEvent read FOnGatherChunksOnServerBegin write FOnGatherChunksOnServerBegin;



  end;



implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

uses uVisualFrame_TPS_ChunkUploader;
var VisualFrame_TPS_ChunkUploader:TVisualFrame_TPS_ChunkUploader;



constructor TSendFileInChunks_TPS_ChunkUploader.Create(AOwner: TComponent);
begin
  inherited;

  if (AOwner is TVisualFrame_TPS_ChunkUploader) then
  VisualFrame_TPS_ChunkUploader:=(AOwner as TVisualFrame_TPS_ChunkUploader);

  FDivideAndGatherFile:=TDivideAndGatherFile_TPS_ChunkUploader.Create(Self);


end;



destructor TSendFileInChunks_TPS_ChunkUploader.Destroy;
begin



  inherited;
end;

function TSendFileInChunks_TPS_ChunkUploader.

                    GatherInfoAboutChunksToFile(

                          AChunksDir,
                          AChunksInfoFileName:string;
                          AFileSize:int64

                    ): Boolean;

var ChunksSDA:TStringDynArray;
    NumberOfChunksAtAll: Integer;
    JsonObject:TJsonObject;
    i: Integer;
    TempFilename:string;
    CurrentChunkNumber:string;
    ChunksInfoString: string;
    ChunksInfoPath:string;



begin

Result:=false;
//
ChunksSDA:=TDirectory.GetFiles(AChunksDir);
NumberOfChunksAtAll:=Length(ChunksSDA);
JSONObject:=TJSONObject.Create;


try

JSONObject.AddPair( (TJSONPair.Create('ChunksNumber',NumberOfChunksAtAll.ToString )) );
JSONObject.AddPair( (TJSONPair.Create('FileSize',AFileSize.ToString )) );

for i := 0 to High(ChunksSDA) do

      begin
      TempFilename:=ExtractFileName(ChunksSDA[i]);
      if not TempFilename.Contains('chunk')  then Continue;  // All filenames should contain chunk word

      CurrentChunkNumber:=
      TCreateUniqueName_TPS_ChunkUploader.GetParamValueFromFileName(TempFilename,'chunk');  // No errror handling here - could be troubles

      JSONObject.AddPair( (TJSONPair.Create(TempFilename,CurrentChunkNumber )) );
      end;


ChunksInfoString:=JSONObject.ToString;
ChunksInfoPath:=AChunksDir+'\'+AChunksInfoFileName;//'ChunksInfo.txt';
TFile.WriteAllText(ChunksInfoPath,ChunksInfoString);


// success if...

if ( TFile.Exists(ChunksInfoPath) ) and (ChunksInfoString<>'') then
Result:=true;


finally
  FreeAndNil(JSONObject);
end;


end;



function TSendFileInChunks_TPS_ChunkUploader.SendChunksInfoFile(ATempDirOnServer, AChunksInfoFilePath:string): Boolean;
begin
//

Result:=False;

Assert(Assigned(VisualFrame_TPS_ChunkUploader),'Please assign VisualFrame_TPS_ChunkUploader ');


    with VisualFrame_TPS_ChunkUploader do begin

    HTTPRequests.SendChunksInfoFile(ATempDirOnServer,AChunksInfoFilePath);
    Result:=true;
    end;





end;





function TSendFileInChunks_TPS_ChunkUploader.
SendFileInChunks(AFilePath: String; AChunkSize:Int64 ): Boolean;


var FileSize:Int64;
    ChunksInfoFileName:string;
    idUserString:String;

begin

Result:=false;
//Checks
Assert(AFilePath<>'','Please fill AFilePath');
Assert(AChunkSize<>0,'Please fill AChunkSize');

//GetFileSize
FileSize:=GetFileSize(AFilePath);

// Create output dir on server
FRelativeOutputDirOnServer:=VisualFrame_TPS_ChunkUploader.Directories.CreateOutputDirOnServer();

  if FileSize<AChunkSize then

  begin

  if Assigned(FOnUploadInChunksBegin) then FOnUploadInChunksBegin(Self,FileSize);

  if SendOneWholeFile(AFilePath,FRelativeOutputDirOnServer) then
  Result:=true;

  if Assigned(FOnUploadInChunksEnd) then FOnUploadInChunksEnd(Self,FileSize);

  end

   else

  if FileSize>=AChunkSize then

  begin

     //CreateAllDirs
      with VisualFrame_TPS_ChunkUploader.Directories do begin

      OriginFileName:=ExtractFileName(AFilePath);
      CreateUniqueNameFromOriginName();


      FRelativeTempDirOnServer:=CreateTempDirOnServer();
      FAbsoluteTempDirOnClient:=CreateTempDirOnClient();

      end;



      try
      //MainWork

      ChunksInfoFileName:='ChunksInfo.txt';

      FDivideAndGatherFile.DivideFileInChunks(AFilePath,AChunkSize,FAbsoluteTempDirOnClient);
      GatherInfoAboutChunksToFile(FAbsoluteTempDirOnClient,ChunksInfoFileName,FileSize);
      //SendChunksInfoFile(FRelativeTempDirOnServer,FAbsoluteTempDirOnClient+ChunksInfoFileName); // <<Deprecated

      // Send Chunks To Server and Gather Them

        with VisualFrame_TPS_ChunkUploader.HTTPRequests do begin

        if Assigned(FOnUploadInChunksBegin) then FOnUploadInChunksBegin(Self,FileSize);

        SendManyFiles(FAbsoluteTempDirOnClient,FRelativeTempDirOnServer);

        // check if all chunks in proper directory on server
        if IsCheckChunksIntegrityOk(FRelativeTempDirOnServer,ChunksInfoFileName) then

           begin

           //Preparing params
           idUserString:=VisualFrame_TPS_ChunkUploader.HTTP_ConnectionParams.IDUser;

           if Assigned(FOnGatherChunksOnServerBegin) then FOnGatherChunksOnServerBegin(Self,FileSize);

           //Gather Chunks OnServer In One File
           if GatherFileFromChunks(

                    FRelativeTempDirOnServer,
                    FRelativeOutputDirOnServer,
                    ExtractFileName(AFilePath),
                    idUserString

            ) then Result:=true;

           if Assigned(FOnUploadInChunksEnd) then FOnUploadInChunksEnd(Self,FileSize);

           end;

        end;


      finally

      //Delete All Temporary Dirs

      if TDirectory.Exists(FAbsoluteTempDirOnClient)
      then TDirectory.Delete(FAbsoluteTempDirOnClient,true);

      VisualFrame_TPS_ChunkUploader.HTTPRequests.
      DeleteDirOnServer(FRelativeTempDirOnServer);

      end;


end;








end;

function TSendFileInChunks_TPS_ChunkUploader.SendFileInChunksSafeFromInterruptions(AFilePath: String;
  AChunkSize: Int64): Boolean;

// ATTENTION PLEASE - THIS FUNCTION NOT FINISHED FOR THE MOMENT

var FileSize:Int64;
    ChunksInfoFileName:string;
    idUserString:String;

    LastSuccesfulChunkNumber:Integer;

begin
{
Result:=false;
//Checks
Assert(AFilePath<>'','Please fill AFilePath');
Assert(AChunkSize<>0,'Please fill AChunkSize');


FFileName:=ExtractFileName(AFilePath);

//GetFileSize
FileSize:=GetFileSize(AFilePath);

with VisualFrame_TPS_ChunkUploader do begin

// Create output dir on server
FRelativeOutputDirOnServer:=Directories.CreateOutputDirOnServer();

   if  FilesInterruptedNotUploaded.FileNamesSL.IndexOf( ExtractFileName(AFilePath) )<>-1 then

     begin

       LastSuccesfulChunkNumber:=
       FilesInterruptedNotUploaded.NumberOfLastSuccessfullUploadedChunk;


        with HTTPRequests do begin

        if Assigned(FOnUploadInChunksBegin) then FOnUploadInChunksBegin(Self,FileSize);

        SendManyFilesFromLastSuccessfull(LastSuccesfulChunkNumber);

        // check if all chunks in proper directory on server
        if IsCheckChunksIntegrityOk(FRelativeTempDirOnServer,ChunksInfoFileName) then

           begin

           //Preparing params
           idUserString:=HTTP_ConnectionParams.IDUser;

           if Assigned(FOnGatherChunksOnServerBegin) then FOnGatherChunksOnServerBegin(Self,FileSize);

           //Gather Chunks OnServer In One File
           if GatherFileFromChunks(

                    FRelativeTempDirOnServer,
                    FRelativeOutputDirOnServer,
                    FFileName,
                    idUserString

            ) then Result:=true;

           if Assigned(FOnUploadInChunksEnd) then FOnUploadInChunksEnd(Self,FileSize);

           end;

        end;




    end;

end;

begin



  if FileSize<AChunkSize then

  begin

  if Assigned(FOnUploadInChunksBegin) then FOnUploadInChunksBegin(Self,FileSize);

  if SendOneWholeFile(AFilePath,FRelativeOutputDirOnServer) then
  Result:=true;

  if Assigned(FOnUploadInChunksEnd) then FOnUploadInChunksEnd(Self,FileSize);

  end

   else

  if FileSize>=AChunkSize then

  begin

     //CreateAllDirs
      with VisualFrame_TPS_ChunkUploader.Directories do begin

      OriginFileName:=ExtractFileName(AFilePath);
      CreateUniqueNameFromOriginName();

      FFileName:=OriginFileName; // for local using

      FRelativeTempDirOnServer:=CreateTempDirOnServer();
      FAbsoluteTempDirOnClient:=CreateTempDirOnClient();

      end;



      try
      //MainWork

      ChunksInfoFileName:='ChunksInfo.txt';

      FDivideAndGatherFile.DivideFileInChunks(AFilePath,AChunkSize,FAbsoluteTempDirOnClient);
      GatherInfoAboutChunksToFile(FAbsoluteTempDirOnClient,ChunksInfoFileName,FileSize);
      //SendChunksInfoFile(FRelativeTempDirOnServer,FAbsoluteTempDirOnClient+ChunksInfoFileName); // <<Deprecated

      // Send Chunks To Server and Gather Them

        with VisualFrame_TPS_ChunkUploader.HTTPRequests do begin

        if Assigned(FOnUploadInChunksBegin) then FOnUploadInChunksBegin(Self,FileSize);

        SendManyFiles(FAbsoluteTempDirOnClient,FRelativeTempDirOnServer);

        // check if all chunks in proper directory on server
        if IsCheckChunksIntegrityOk(FRelativeTempDirOnServer,ChunksInfoFileName) then

           begin

           //Preparing params
           idUserString:=VisualFrame_TPS_ChunkUploader.HTTP_ConnectionParams.IDUser;

           if Assigned(FOnGatherChunksOnServerBegin) then FOnGatherChunksOnServerBegin(Self,FileSize);

           //Gather Chunks OnServer In One File
           if GatherFileFromChunks(

                    FRelativeTempDirOnServer,
                    FRelativeOutputDirOnServer,
                    FFileName,
                    idUserString

            ) then Result:=true;

           if Assigned(FOnUploadInChunksEnd) then FOnUploadInChunksEnd(Self,FileSize);

           end;

        end;


      finally

      //Delete All Temporary Dirs else leave it as it is...

          if Result then begin

          if TDirectory.Exists(FAbsoluteTempDirOnClient)
          then TDirectory.Delete(FAbsoluteTempDirOnClient,true);

          VisualFrame_TPS_ChunkUploader.HTTPRequests.
          DeleteDirOnServer(FRelativeTempDirOnServer);

          end;


      end;




end;
end;

}
end;

function TSendFileInChunks_TPS_ChunkUploader.SendOneWholeFile(AFilePath:String; ARelativeDirOnServer:String):Boolean;

var
    idUserString:String;
    FileName:string;

begin

Result:=False;
Assert( Assigned(VisualFrame_TPS_ChunkUploader), ' Please assign VisualFrame_TPS_ChunkUploader' );


    with VisualFrame_TPS_ChunkUploader do begin


    idUserString:=HTTP_ConnectionParams.IDUser;
    FileName:=ExtractFileName(AFilePath);

    //checks

    Assert(idUserString<>'','Please fill idUserString');
    Assert(AFilePath<>'','Please fill AFilePath');
    Assert(FileName<>'','Please fill FOriginFileName');

    HTTPRequests.
    SendOneWholeFile(AFilePath,ARelativeDirOnServer,FileName,idUserString);

    Result:=true;
    end;




end;



function TSendFileInChunks_TPS_ChunkUploader.GetFileSize(AFilePath: String): Int64;
var
  FS: TFileStream;


begin


  try

     Result:=-1;

     FS := TFileStream.Create(AFilePath, fmOpenRead);

      try
        Result:=FS.Size;
      finally
        FS.Free;
      end;

  except

         on E:EStreamError do

         begin

         Result := -1;

          raise Exception.Create('This is EStreamError EClassName'+E.ClassName+' '
          +'EMessage '+E.Message);

         end;


  on E:Exception do

        begin
           Result := -1;


           raise Exception.Create(E.ClassName+' Exception Raised : '
         +#13#10+#13#10+E.Message);

        end;

  end;

end;





end.
