unit uDivideAndGatherFile_TPS_ChunkUploader;

interface

uses
  System.SysUtils, System.Classes,System.IOUtils,System.Types,
  Contnrs,
  Winapi.Windows, Winapi.Messages, System.Variants,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,


  //myUnits
  uSortStringList_TPS_ChunkUploader,
  uCreateUniqueName_TPS_ChunkUploader
;

type
  TDivideAndGatherFile_TPS_ChunkUploader = class(TDataModule)
  private



    { Private declarations }
  public
    { Public declarations }

    constructor Create(AOwner: TComponent); override;

    function DivideFileInChunks(AFilepath: string; AChunkSize: Int64; ADirForChunks: string):Boolean;
    function GatherFileFromChunks(ARelativeChunksDir, ARelativeOutputDir, AOutputFileName: string):Boolean;

  end;




implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

uses uSendFileInChunks_TPS_ChunkUploader;
var SendFileInChunks_UnitVar:TSendFileInChunks_TPS_ChunkUploader;


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

if (AOwner is TSendFileInChunks_TPS_ChunkUploader) then
SendFileInChunks_UnitVar:=(AOwner as TSendFileInChunks_TPS_ChunkUploader);

end;



function TDivideAndGatherFile_TPS_ChunkUploader.

    DivideFileInChunks(

      AFilepath: string;
      AChunkSize: Int64;
      ADirForChunks: string

      ):Boolean;

var
    FS:TFileStream;
    MS:TMemoryStream;
    FileName:string;

    ChunkNumber:Int64;
    ByteToRead: Int64;
    ReadSize: Int64;
    NewFileName: string;


begin

Result:=false;

//checks
Assert(AFilepath<>'','Please fill Afilepath');
Assert(AChunkSize<>0,'Please fill AChunkSize');
Assert(ADirForChunks<>'','Please fill ADirForChunks');




      try


          FS:=TFile.OpenRead(AFilepath);
          FS.Position:=0;

          MS:=TMemoryStream.Create;
          MS.Position:=0;
          FileName:=ExtractFileName(AFilepath);

          // On divide begin
          if Assigned(SendFileInChunks_UnitVar) then
          if Assigned(SendFileInChunks_UnitVar.OnDivideInChunksOnClientBegin) then
          SendFileInChunks_UnitVar.OnDivideInChunksOnClientBegin(Self,FS.Size);


            try


          ByteToRead:=FS.Size;


          ChunkNumber:=0;

          while ByteToRead>0 do

           begin


              ReadSize := AChunkSize;
              if ReadSize > ByteToRead then ReadSize := ByteToRead;

              MS.CopyFrom(FS,ReadSize);

              NewFileName:='{chunk='+ChunkNumber.ToString+'}';

              if Length(ADirForChunks+'\'+NewFileName)>MAX_PATH then // 255 or 260
              raise Exception.Create('FileName '+FileName+' too long - try shorten it');


              MS.SaveToFile(ADirForChunks+'\'+NewFileName);
              MS.Clear;
              MS.Position:=0;

              Inc(ChunkNumber);

              //ByteToRead:=ByteToRead-ReadSize; // or dec...

              dec(ByteToRead,ReadSize);

             // On divide
              if Assigned(SendFileInChunks_UnitVar) then
              if Assigned(SendFileInChunks_UnitVar.OnDivideInChunksOnClientBegin) then
              SendFileInChunks_UnitVar.OnDivideInChunksOnClient(Self,FS.Size-ByteToRead);

           end;

          Result:=true;

          finally

              // On divide end
              if Assigned(SendFileInChunks_UnitVar) then
              if Assigned(SendFileInChunks_UnitVar.OnDivideInChunksOnClientBegin) then
              SendFileInChunks_UnitVar.OnDivideInChunksOnClient(Self,FS.Size);


          FreeAndNil(FS);
          FreeAndNil(MS);

          end;




    except

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

    on E:Exception do

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

    end;



end;




function TDivideAndGatherFile_TPS_ChunkUploader.

GatherFileFromChunks(

              ARelativeChunksDir:string;
              ARelativeOutputDir: string;
              AOutputFileName:string

      ):Boolean;

var FilenamesWithChunksSDA:TStringDynArray;

    ChunkPathesSL:TStringList;
    ChunkNumbersSL:TStringList;

    ChunksPath:string;

    i: Integer;

    SLObjectList:TObjectList;

    SortStringList:TSortStringList;
    ChunkNumber: string;

    FS:TFileStream;
    AppServerPath:string;

    MS: TmemoryStream;
    Outputfilepath: string;


begin

Result:=False;

//checks
Assert(AOutputFileName<>'','Please fill AOutputFilename');


//---------------------1 PREPARATION---------------------

//Create
ChunkPathesSL:=TStringList.Create;
ChunkNumbersSL:=TStringList.Create;
SLObjectList:=TObjectList.Create;
SortStringList:=TSortStringList.Create(Self);

try
        AppServerPath:=ExtractFilePath(Application.ExeName);

        //Gathering all files from directory
        FilenamesWithChunksSDA:=TDirectory.GetFiles(AppServerPath+ARelativeChunksDir);

        // Filling String lists...


              ChunkPathesSL.Clear;
              for i := Low(FilenamesWithChunksSDA) to High(FilenamesWithChunksSDA) do
              ChunkPathesSL.Add(FilenamesWithChunksSDA[i]);

              ChunkNumbersSL.Clear;
              for i := 0 to ChunkPathesSL.Count-1 do
              begin

              ChunkNumber:=
              TCreateUniqueName_TPS_ChunkUploader.GetParamValueFromFileName(ChunkPathesSL[i],'chunk' );
              ChunkNumbersSL.Add(ChunkNumber);

              end;



        //IntegrityCheck - check chunks and their sizes

        //Sorting

              SLObjectList.Add(ChunkPathesSL);
              SLObjectList.OwnsObjects:=False;

              // Sorting at last
              SortStringList.SortOneOrderManyStringLists(
              ChunkNumbersSL,
              SLObjectList,
              'int64',
              true
              );



        //------------------------2  GATHERING-------------------

        {so after all preparations we suggest that fileChunks pathes in ChunkPathesSL }

        //AppServerPath:=ExtractFilePath(Application.ExeName);

        if not TDirectory.Exists(AppServerPath+ARelativeOutputDir) then
        TDirectory.CreateDirectory(AppServerPath+ARelativeOutputDir);

        Outputfilepath:=AppServerPath+ARelativeOutputDir+'\'+AOutputFileName;

        FS:=TFile.Create(Outputfilepath);
        FS.Position:=0;


        MS:=TMemoryStream.Create;

            try


                for i := 0 to ChunkPathesSL.Count-1 do

                begin

                MS.LoadFromFile(ChunkPathesSL[i]);

                FS.CopyFrom(MS,MS.Size);

                MS.Clear;
                MS.Position:=0;

                end;



            finally

               FreeAndNil(FS);
               FreeAndNil(MS);

            end;


        Result:=True;

finally
//Freeing
FreeAndNil(ChunkPathesSL);
FreeAndNil(ChunkNumbersSL);
FreeAndNil(SLObjectList);
FreeAndNil(SortStringList);
end;


end;




end.
