UPD: Rewrite cm_FileSpliter command as TFileSourceOperation

This commit is contained in:
Alexander Koblov 2011-03-27 07:24:03 +00:00
commit e9fda4ec49
12 changed files with 789 additions and 336 deletions

View file

@ -203,7 +203,7 @@
<PackageName Value="viewerpackage"/>
</Item7>
</RequiredPackages>
<Units Count="106">
<Units Count="110">
<Unit0>
<Filename Value="doublecmd.lpr"/>
<IsPartOfProject Value="True"/>
@ -384,6 +384,7 @@
<IsPartOfProject Value="True"/>
<ComponentName Value="frmSplitter"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="fSplitter"/>
</Unit23>
<Unit24>
@ -858,6 +859,26 @@
<ResourceBaseClass Value="Frame"/>
<UnitName Value="fWfxPluginCopyMoveOperationOptions"/>
</Unit105>
<Unit106>
<Filename Value="newdesign\ufilesystemcombineoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSystemCombineOperation"/>
</Unit106>
<Unit107>
<Filename Value="newdesign\ufilesourcecombineoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSourceCombineOperation"/>
</Unit107>
<Unit108>
<Filename Value="newdesign\ufilesourcesplitoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSourceSplitOperation"/>
</Unit108>
<Unit109>
<Filename Value="newdesign\ufilesystemsplitoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSystemSplitOperation"/>
</Unit109>
</Units>
</ProjectOptions>
<CompilerOptions>

View file

@ -8,7 +8,7 @@
contributors:
Copyright (C) 2008-2010 Koblov Alexander (Alexx2000@mail.ru)
Copyright (C) 2008-2011 Koblov Alexander (Alexx2000@mail.ru)
}
unit fFileOpDlg;
@ -72,6 +72,7 @@ type
procedure InitializeMoveOperation(Operation: TFileSourceOperation);
procedure InitializeDeleteOperation(Operation: TFileSourceOperation);
procedure InitializeWipeOperation(Operation: TFileSourceOperation);
procedure InitializeSplitOperation(Operation: TFileSourceOperation);
procedure InitializeCombineOperation(Operation: TFileSourceOperation);
procedure InitializeCalcChecksumOperation(Operation: TFileSourceOperation);
procedure InitializeTestArchiveOperation(Operation: TFileSourceOperation);
@ -79,6 +80,7 @@ type
procedure UpdateMoveOperation(Operation: TFileSourceOperation);
procedure UpdateDeleteOperation(Operation: TFileSourceOperation);
procedure UpdateWipeOperation(Operation: TFileSourceOperation);
procedure UpdateSplitOperation(Operation: TFileSourceOperation);
procedure UpdateCombineOperation(Operation: TFileSourceOperation);
procedure UpdateCalcChecksumOperation(Operation: TFileSourceOperation);
procedure UpdateTestArchiveOperation(Operation: TFileSourceOperation);
@ -107,6 +109,7 @@ uses
uFileSourceMoveOperation,
uFileSourceDeleteOperation,
uFileSourceWipeOperation,
uFileSourceSplitOperation,
uFileSourceCombineOperation,
uFileSourceCalcChecksumOperation,
uFileSourceTestArchiveOperation,
@ -225,6 +228,8 @@ begin
InitializeDeleteOperation(Operation);
fsoWipe:
InitializeWipeOperation(Operation);
fsoSplit:
InitializeSplitOperation(Operation);
fsoCombine:
InitializeCombineOperation(Operation);
fsoCalcChecksum:
@ -333,6 +338,8 @@ begin
UpdateDeleteOperation(Operation);
fsoWipe:
UpdateWipeOperation(Operation);
fsoSplit:
UpdateSplitOperation(Operation);
fsoCombine:
UpdateCombineOperation(Operation);
fsoCalcChecksum:
@ -508,6 +515,12 @@ begin
lblFrom.Caption := rsDlgDeleting;
end;
procedure TfrmFileOp.InitializeSplitOperation(Operation: TFileSourceOperation);
begin
Caption := rsDlgSplit;
InitializeControls([fodl_from_lbl, fodl_to_lbl, fodl_first_pb, fodl_second_pb]);
end;
procedure TfrmFileOp.InitializeCombineOperation(Operation: TFileSourceOperation);
begin
Caption := rsDlgCombine;
@ -602,6 +615,25 @@ begin
end;
end;
procedure TfrmFileOp.UpdateSplitOperation(Operation: TFileSourceOperation);
var
SplitOperation: TFileSourceSplitOperation;
SplitStatistics: TFileSourceSplitOperationStatistics;
begin
SplitOperation := Operation as TFileSourceSplitOperation;
SplitStatistics := SplitOperation.RetrieveStatistics;
with SplitStatistics do
begin
lblFileNameFrom.Caption := MinimizeFilePath(CurrentFileFrom, lblFileNameFrom.Canvas, lblFileNameFrom.Width);
lblFileNameTo.Caption := MinimizeFilePath(CurrentFileTo, lblFileNameTo.Canvas, lblFileNameTo.Width);
SetProgressBytes(pbFirst, CurrentFileDoneBytes, CurrentFileTotalBytes);
SetProgressBytes(pbSecond, DoneBytes, TotalBytes);
SetSpeedAndTime(Operation, RemainingTime, cnvFormatFileSize(BytesPerSecond, True) + 'B');
end;
end;
procedure TfrmFileOp.UpdateCombineOperation(Operation: TFileSourceOperation);
var
CombineOperation: TFileSourceCombineOperation;

View file

@ -1,75 +1,69 @@
object frmSplitter: TfrmSplitter
Left = 542
Height = 302
Top = 242
Width = 480
Left = 283
Height = 240
Top = 122
Width = 399
HorzScrollBar.Page = 464
HorzScrollBar.Range = 369
VertScrollBar.Page = 301
VertScrollBar.Range = 227
ActiveControl = cmbxSize
AutoSize = True
Caption = 'Splitter'
ClientHeight = 302
ClientWidth = 480
ChildSizing.LeftRightSpacing = 6
ChildSizing.TopBottomSpacing = 6
ClientHeight = 240
ClientWidth = 399
Position = poScreenCenter
LCLVersion = '0.9.29'
object prgbrDoIt: TProgressBar
AnchorSideLeft.Control = Owner
AnchorSideRight.Control = Owner
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = Owner
AnchorSideBottom.Side = asrBottom
Left = 6
Height = 27
Top = 269
Width = 468
Anchors = [akLeft, akRight, akBottom]
BorderSpacing.Left = 6
BorderSpacing.Right = 6
BorderSpacing.Bottom = 6
TabOrder = 5
end
object grbxFile: TGroupBox
AnchorSideLeft.Control = Owner
AnchorSideTop.Control = Owner
AnchorSideBottom.Control = grbxSize
AnchorSideRight.Control = btnCancel
AnchorSideRight.Side = asrBottom
Left = 6
Height = 146
Top = 0
Width = 290
Anchors = [akTop, akLeft, akBottom]
Height = 118
Top = 6
Width = 385
Anchors = [akTop, akLeft, akRight]
AutoSize = True
BorderSpacing.Left = 6
BorderSpacing.Bottom = 6
Caption = 'File name'
ClientHeight = 127
ClientWidth = 286
ChildSizing.LeftRightSpacing = 10
ChildSizing.TopBottomSpacing = 4
ClientHeight = 100
ClientWidth = 381
TabOrder = 0
object lbFileSource: TLabel
Left = 6
Height = 18
Top = 0
Width = 71
Left = 10
Height = 14
Top = 4
Width = 52
Caption = 'File source'
ParentColor = False
end
object lbDirTarget: TLabel
AnchorSideTop.Control = edFileSource
AnchorSideTop.Side = asrBottom
Left = 6
Height = 18
Top = 73
Width = 104
BorderSpacing.Top = 22
Left = 10
Height = 14
Top = 55
Width = 78
BorderSpacing.Top = 12
Caption = 'Directory target'
ParentColor = False
end
object edFileSource: TEdit
AnchorSideTop.Control = lbFileSource
AnchorSideTop.Side = asrBottom
Left = 6
Height = 27
Top = 24
Width = 264
BorderSpacing.Top = 6
AnchorSideRight.Side = asrBottom
Left = 10
Height = 21
Top = 22
Width = 360
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 4
ReadOnly = True
TabOrder = 0
end
@ -77,12 +71,12 @@ object frmSplitter: TfrmSplitter
AnchorSideTop.Control = lbDirTarget
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = btnFTChoice
Left = 6
Height = 27
Top = 97
Width = 234
Left = 10
Height = 21
Top = 73
Width = 330
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 6
BorderSpacing.Top = 4
BorderSpacing.Right = 6
TabOrder = 1
end
@ -91,9 +85,9 @@ object frmSplitter: TfrmSplitter
AnchorSideTop.Control = edDirTarget
AnchorSideRight.Control = edFileSource
AnchorSideRight.Side = asrBottom
Left = 246
Left = 346
Height = 23
Top = 97
Top = 73
Width = 24
Anchors = [akTop, akRight]
BorderSpacing.Left = 4
@ -104,25 +98,32 @@ object frmSplitter: TfrmSplitter
end
object grbxSize: TGroupBox
AnchorSideLeft.Control = Owner
AnchorSideRight.Control = grbxWatch
AnchorSideTop.Control = grbxFile
AnchorSideTop.Side = asrBottom
Left = 6
Height = 112
Top = 152
Width = 290
Anchors = [akLeft, akRight, akBottom]
Height = 103
Top = 130
Width = 300
AutoSize = True
BorderSpacing.Left = 6
BorderSpacing.Right = 6
Caption = 'Size and number of parts'
ClientHeight = 93
ClientWidth = 286
ChildSizing.LeftRightSpacing = 6
ChildSizing.TopBottomSpacing = 6
ClientHeight = 85
ClientWidth = 296
Constraints.MinWidth = 300
TabOrder = 1
object cmbxSize: TComboBox
AnchorSideLeft.Control = grbxSize
AnchorSideTop.Control = grbxSize
AnchorSideRight.Side = asrBottom
Left = 6
Height = 29
Top = 8
Width = 262
Height = 21
Top = 6
Width = 276
Anchors = [akTop, akLeft, akRight]
ItemHeight = 0
ItemHeight = 13
ItemIndex = 0
Items.Strings = (
'1457664B - 3.5"'
@ -138,28 +139,40 @@ object frmSplitter: TfrmSplitter
Text = '1457664B - 3.5"'
end
object rbtnKiloB: TRadioButton
AnchorSideLeft.Control = cmbxSize
AnchorSideTop.Control = cmbxSize
AnchorSideTop.Side = asrBottom
Left = 6
Height = 22
Top = 39
Width = 85
Height = 17
Top = 33
Width = 61
BorderSpacing.Top = 6
Caption = 'Kilobytes'
OnChange = rbtnKiloBChange
TabOrder = 1
end
object rbtnMegaB: TRadioButton
Left = 96
Height = 22
Top = 39
Width = 96
AnchorSideLeft.Control = rbtnKiloB
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = rbtnKiloB
Left = 69
Height = 17
Top = 33
Width = 71
BorderSpacing.Left = 2
Caption = 'Megabytes'
OnChange = rbtnKiloBChange
TabOrder = 2
end
object rbtnGigaB: TRadioButton
Left = 192
Height = 22
Top = 39
Width = 90
AnchorSideLeft.Control = rbtnMegaB
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = rbtnMegaB
Left = 142
Height = 17
Top = 33
Width = 66
BorderSpacing.Left = 2
Caption = 'Gigabytes'
OnChange = rbtnKiloBChange
TabOrder = 3
@ -169,12 +182,12 @@ object frmSplitter: TfrmSplitter
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = rbtnMegaB
AnchorSideTop.Side = asrBottom
Left = 117
Height = 27
Top = 63
Left = 89
Height = 21
Top = 58
Width = 72
BorderSpacing.Left = 4
BorderSpacing.Top = 2
BorderSpacing.Top = 8
OnKeyPress = teNumberPartsKeyPress
OnKeyUp = teNumberPartsKeyUp
TabOrder = 4
@ -184,83 +197,43 @@ object frmSplitter: TfrmSplitter
AnchorSideTop.Control = teNumberParts
AnchorSideTop.Side = asrCenter
Left = 6
Height = 18
Top = 67
Width = 107
Height = 14
Top = 61
Width = 79
Caption = 'Number of parts'
ParentColor = False
end
end
object grbxWatch: TGroupBox
AnchorSideLeft.Control = grbxFile
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Owner
AnchorSideRight.Control = Owner
AnchorSideRight.Side = asrBottom
Left = 302
Height = 224
Top = 0
Width = 172
Anchors = [akTop, akLeft, akRight, akBottom]
BorderSpacing.Left = 6
BorderSpacing.Right = 6
Caption = 'Watchtower'
ClientHeight = 205
ClientWidth = 168
TabOrder = 2
object memWatch: TMemo
AnchorSideLeft.Control = grbxWatch
AnchorSideTop.Control = grbxWatch
AnchorSideRight.Control = grbxWatch
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = grbxWatch
AnchorSideBottom.Side = asrBottom
Left = 4
Height = 193
Top = 6
Width = 160
Anchors = [akTop, akLeft, akRight, akBottom]
BorderSpacing.Left = 4
BorderSpacing.Top = 6
BorderSpacing.Right = 4
BorderSpacing.Bottom = 6
Color = clBlack
Font.Color = clYellow
Font.Height = 11
Font.Name = 'MS Shell Dlg'
Font.Pitch = fpVariable
ParentFont = False
ReadOnly = True
ScrollBars = ssAutoBoth
TabOrder = 0
TabStop = False
WordWrap = False
end
end
object btnOK: TButton
AnchorSideRight.Control = btnCancel
Left = 318
AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = btnCancel
Left = 316
Height = 32
Top = 232
Top = 163
Width = 75
Anchors = [akRight, akBottom]
BorderSpacing.Right = 6
BorderSpacing.Bottom = 6
Caption = 'OK'
Default = True
OnClick = btnOKClick
TabOrder = 3
ModalResult = 1
TabOrder = 2
end
object btnCancel: TButton
AnchorSideRight.Control = grbxWatch
AnchorSideLeft.Control = grbxSize
AnchorSideLeft.Side = asrBottom
AnchorSideRight.Side = asrBottom
Left = 399
AnchorSideBottom.Control = grbxSize
AnchorSideBottom.Side = asrBottom
Left = 316
Height = 32
Top = 232
Top = 201
Width = 75
Anchors = [akRight, akBottom]
Anchors = [akLeft, akBottom]
BorderSpacing.Left = 10
Cancel = True
Caption = 'Exit'
Caption = 'Cancel'
ModalResult = 2
TabOrder = 4
TabOrder = 3
end
end

View file

@ -9,6 +9,5 @@ TFRMSPLITTER.RBTNKILOB.CAPTION=Kilobytes
TFRMSPLITTER.RBTNMEGAB.CAPTION=Megabytes
TFRMSPLITTER.RBTNGIGAB.CAPTION=Gigabytes
TFRMSPLITTER.LBLNUMBERPARTS.CAPTION=Number of parts
TFRMSPLITTER.GRBXWATCH.CAPTION=Watchtower
TFRMSPLITTER.BTNOK.CAPTION=OK
TFRMSPLITTER.BTNCANCEL.CAPTION=Exit
TFRMSPLITTER.BTNCANCEL.CAPTION=Cancel

View file

@ -17,7 +17,9 @@ unit fSplitter;
interface
uses
SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ComCtrls;
SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ComCtrls,
uFileSource,
uFile;
type
@ -34,56 +36,103 @@ type
btnFTChoice: TButton;
grbxSize: TGroupBox;
cmbxSize: TComboBox;
grbxWatch: TGroupBox;
memWatch: TMemo;
btnOK: TButton;
btnCancel: TButton;
prgbrDoIt: TProgressBar;
rbtnKiloB: TRadioButton;
rbtnMegaB: TRadioButton;
rbtnGigaB: TRadioButton;
procedure btnFTChoiceClick(Sender: TObject);
procedure btnOKClick(Sender: TObject);
procedure cmbxSizeCloseUp(Sender: TObject);
procedure cmbxSizeKeyPress(Sender: TObject; var Key: char);
procedure cmbxSizeKeyUp(Sender: TObject; var Key: char; Shift: TShiftState);
procedure FormCreate(Sender: TObject);
procedure rbtnKiloBChange(Sender: TObject);
procedure SetNumberOfPart;
procedure SetSizeOfPart;
procedure teNumberPartsKeyPress(Sender: TObject; var Key: char);
procedure teNumberPartsKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure teNumberPartsKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
function StrConvert(str:string):int64;
//test for correct file size format;
function StrConvert(str: String): Int64;
public
{ Public declarations }
end;
function ShowSplitterFileForm(const sFile: TStringList; const sTargetDir: String): Boolean;
function ShowSplitterFileForm(aFileSource: IFileSource; var aFile: TFile; const TargetPath: UTF8String): Boolean;
implementation
{$R *.lfm}
uses
LCLProc, uLng, uClassesEx, uOSUtils;
LCLProc, uLng, uClassesEx, uOSUtils, uFileProcs, uOperationsManager,
uFileSourceSplitOperation, fFileOpDlg, uShowMsg;
function ShowSplitterFileForm(const sFile: TStringList; const sTargetDir: String): Boolean;
function ShowSplitterFileForm(aFileSource: IFileSource; var aFile: TFile; const TargetPath: UTF8String): Boolean;
var
iVolumeNumber: Integer;
iFileSize, iVolumeSize: Int64;
Operation: TFileSourceSplitOperation = nil;
ProgressDialog: TfrmFileOp;
OperationHandle: TOperationHandle;
begin
with TfrmSplitter.Create(Application) do
begin
try
edFileSource.Text:= sFile[0];
edDirTarget.Text:= ExtractFileDir(sTargetDir);
edFileSource.Text:= aFile.FullPath;
edDirTarget.Text:= TargetPath;
SetNumberOfPart;
rbtnKiloB.Enabled:=false;
rbtnMegaB.Enabled:=false;
rbtnGigaB.Enabled:=false;
// Show form
Result:= (ShowModal = mrOK);
if Result then
begin
iVolumeSize:= StrConvert(cmbxSize.Text);
if iVolumeSize <= 0 then
begin
msgError(rsSplitErrFileSize); // Incorrect file size format!
Exit;
end;
iFileSize:= mbFileSize(edFileSource.Text);
if iVolumeSize >= iFileSize then
begin
msgError(rsSplitErrSplitFile); // Unable to split the file!
Exit;
end;
if not mbForceDirectory(ExtractFileDir(edDirTarget.Text)) then
begin
msgError(rsSplitErrDirectory); // Unable to create target directory!
Exit;
end;
iVolumeNumber:= StrToInt(teNumberParts.Text);
if (iVolumeNumber = 0) then
begin
msgError(rsSplitErrSplitFile); // Unable to split the file!
Exit;
end;
if iVolumeNumber > 100 then
begin
if MessageDlg(Caption, rsSplitMsgManyParts, mtWarning, mbYesNo, 0) <> mrYes then
begin
Exit; // Too many parts
end;
end;
try
Operation:= aFileSource.CreateSplitOperation(aFile, edDirTarget.Text) as TFileSourceSplitOperation;
if Assigned(Operation) then
begin
Operation.VolumeSize:= iVolumeSize;
Operation.VolumeNumber:= iVolumeNumber;
OperationHandle:= OperationsManager.AddOperation(Operation, ossAutoStart);
ProgressDialog:= TfrmFileOp.Create(OperationHandle);
ProgressDialog.Show;
end;
finally
FreeThenNil(aFile);
end;
end;
finally
Free;
end;
@ -181,146 +230,6 @@ begin
Result:=iRet;
end;
procedure TfrmSplitter.btnOKClick(Sender: TObject);
var
iFileSize:int64;
i,num:integer;
fSource: TFileStreamEx = nil;
fDest: TFileStreamEx = nil;
begin
memWatch.Clear;
prgbrDoIt.Position:=0;
iFileSize:=StrConvert(cmbxSize.Text);
if iFileSize<=0 then
begin
memWatch.Append(rsSplitErrFileSize);
//Incorrect file size format!
exit;
end;
if not mbDirectoryExists(edDirTarget.Text) then
begin
if not mbCreateDir(edDirTarget.Text) then
begin
memWatch.Append(rsSplitErrDirectory);
//Unable to create target directory!
exit;
end;
end;
if edDirTarget.Text[Length(edDirTarget.Text)]<>PathDelim then
edDirTarget.Text:=edDirTarget.Text+PathDelim;
try
fSource:= TFileStreamEx.Create(edFileSource.Text,fmOpenRead);
except
on E: EFOpenError do
begin
MessageDlg(Caption, rsMsgErrEOpen + ': ' + E.Message, mtError, [mbOK], 0);
Exit;
end;
on E: EReadError do
begin
MessageDlg(Caption, rsMsgErrERead + ': ' + E.Message, mtError, [mbOK], 0);
Exit;
end;
end;
try
if iFileSize >= fSource.Size then
begin
memWatch.Append(rsSplitErrSplitFile);
//Unable to split the file!
exit;
end;
prgbrDoIt.Max:=(fSource.Size div iFileSize);
if prgbrDoIt.Max=0 then
begin
memWatch.Append(rsSplitErrSplitFile);
//Unable to split the file!
exit;
end;
if prgbrDoIt.Max > 100 then
begin
if MessageDlg(Caption, rsSplitMsgManyParts, mtWarning, mbYesNo, 0) <> mrYes then
begin
memWatch.Append(rsSplitErrTooManyParts);
// Too many parts
Exit;
end;
end;
num:=0;
i:=prgbrDoIt.Max;
while i>=1 do
begin
i:=i div 10;
inc(num);
end;
i:=0;
while i<=prgbrDoIt.Max-1 do
try
fDest:=TFileStreamEx.Create(
edDirTarget.Text+ExtractFileName(edFileSource.Text)+
'.'+Format('%.*d',[num+1,i])+'.split'
,fmCreate);
try
fSource.Seek(iFileSize*i,soFromBeginning);
fDest.CopyFrom(fSource,iFileSize);
memWatch.Append(rsSplitMsgCreated+' '+
edDirTarget.Text+ExtractFileName(edFileSource.Text)+
'.'+Format('%.*d',[num+1,i])+'.split'+
' ... '+rsSplitMsgSize+' '+
IntToStr(iFileSize)+'b');
prgbrDoIt.Position:=prgbrDoIt.Position+1;
finally
FreeThenNil(fDest);
end;
inc(i);
except
on E: EFCreateError do
begin
MessageDlg(Caption, rsMsgErrECreate + ': ' + E.Message, mtError, [mbOK], 0);
Exit;
end;
on E: EWriteError do
begin
MessageDlg(Caption, rsMsgErrEWrite + ': ' + E.Message, mtError, [mbOK], 0);
Exit;
end;
end;
if (fSource.Position)<fSource.Size then
try
fDest:=TFileStreamEx.Create(
edDirTarget.Text+ExtractFileName(edFileSource.Text)+
'.'+Format('%.*d',[num+1,i])+'.split'
,fmCreate);
try
fDest.CopyFrom(fSource,fSource.Size-fSource.Position);
memWatch.Append(rsSplitMsgCreated+' '+
edDirTarget.Text+ExtractFileName(edFileSource.Text)+
'.'+Format('%.*d',[num+1,i])+'.split ... '+
rsSplitMsgSize+' '+
IntToStr(fSource.Size-(iFileSize*i))+'b');
prgbrDoIt.Position:=prgbrDoIt.Position+1;
finally
FreeThenNil(fDest);
end;
except
on E: EFCreateError do
begin
MessageDlg(Caption, rsMsgErrECreate + ': ' + E.Message, mtError, [mbOK], 0);
Exit;
end;
on E: EWriteError do
begin
MessageDlg(Caption, rsMsgErrEWrite + ': ' + E.Message, mtError, [mbOK], 0);
Exit;
end;
end;
finally
FreeThenNil(fSource);
end;
end;
procedure TfrmSplitter.cmbxSizeCloseUp(Sender: TObject);
begin
SetNumberOfPart;
@ -352,6 +261,13 @@ begin
end;
end;
procedure TfrmSplitter.FormCreate(Sender: TObject);
begin
rbtnKiloB.Enabled:= False;
rbtnMegaB.Enabled:= False;
rbtnGigaB.Enabled:= False;
end;
procedure TfrmSplitter.rbtnKiloBChange(Sender: TObject);
begin
SetNumberOfPart;

View file

@ -65,6 +65,8 @@ type
TargetPath: String): TFileSourceOperation;
function CreateDeleteOperation(var FilesToDelete: TFiles): TFileSourceOperation;
function CreateWipeOperation(var FilesToWipe: TFiles): TFileSourceOperation;
function CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation;
function CreateCombineOperation(var theSourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation;
function CreateCreateDirectoryOperation(BasePath: String; DirectoryPath: String): TFileSourceOperation;
@ -221,6 +223,8 @@ type
TargetPath: String): TFileSourceOperation; virtual;
function CreateDeleteOperation(var FilesToDelete: TFiles): TFileSourceOperation; virtual;
function CreateWipeOperation(var FilesToWipe: TFiles): TFileSourceOperation; virtual;
function CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation;
function CreateCombineOperation(var theSourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation; virtual;
function CreateCreateDirectoryOperation(BasePath: String; DirectoryPath: String): TFileSourceOperation; virtual;
@ -614,6 +618,12 @@ begin
Result := nil;
end;
function TFileSource.CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation;
begin
Result := nil;
end;
function TFileSource.CreateCombineOperation(var theSourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation;
begin

View file

@ -19,6 +19,7 @@ type
fsoMove, // Move/rename files within the same file source.
fsoDelete,
fsoWipe,
fsoSplit,
fsoCombine,
fsoCreateDirectory,
//fsoCreateFile,

View file

@ -0,0 +1,181 @@
unit uFileSourceSplitOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, syncobjs,
uFileSourceOperation,
uFileSourceOperationTypes,
uFileSource,
uFile,
uFileSourceCopyOperation;
type
TFileSourceSplitOperationStatistics = TFileSourceCopyOperationStatistics;
{en
Operation that split file within the same file source.
}
TFileSourceSplitOperation = class(TFileSourceOperation)
private
FStatistics: TFileSourceSplitOperationStatistics;
FStatisticsAtStartTime: TFileSourceSplitOperationStatistics;
FStatisticsLock: TCriticalSection; //<en For synchronizing statistics.
FFileSource: IFileSource;
FSourceFile: TFile;
FTargetPath: String;
FVolumeSize: Int64;
FVolumeNumber: LongInt;
protected
function GetID: TFileSourceOperationType; override;
procedure DoReloadFileSources; override;
procedure UpdateStatistics(var NewStatistics: TFileSourceSplitOperationStatistics);
procedure UpdateStatisticsAtStartTime; override;
property FileSource: IFileSource read FFileSource;
property SourceFile: TFile read FSourceFile;
property TargetPath: String read FTargetPath;
public
{en
@param(aFileSource
File source within which the operation should take place.
Class takes ownership of the pointer.)
@param(aSourceFile
The file which are to be splitted.
Class takes ownership of the pointer.)
@param(aTargetPath
Target path for splitted files.)
}
constructor Create(aFileSource: IFileSource;
var aSourceFile: TFile;
aTargetPath: String); virtual reintroduce;
destructor Destroy; override;
function RetrieveStatistics: TFileSourceSplitOperationStatistics;
property VolumeSize: Int64 read FVolumeSize write FVolumeSize;
property VolumeNumber: LongInt read FVolumeNumber write FVolumeNumber;
end;
implementation
uses
uDCUtils;
// -- TFileSourceSplitOperation ------------------------------------------------
constructor TFileSourceSplitOperation.Create(aFileSource: IFileSource;
var aSourceFile: TFile;
aTargetPath: String);
begin
with FStatistics do
begin
CurrentFileFrom := '';
CurrentFileTo := '';
TotalFiles := 0;
DoneFiles := 0;
TotalBytes := 0;
DoneBytes := 0;
CurrentFileTotalBytes := 0;
CurrentFileDoneBytes := 0;
BytesPerSecond := 0;
RemainingTime := 0;
end;
FStatisticsLock := TCriticalSection.Create;
inherited Create(aFileSource);
FFileSource := aFileSource;
FSourceFile := aSourceFile;
aSourceFile := nil;
FTargetPath := aTargetPath;
end;
destructor TFileSourceSplitOperation.Destroy;
begin
inherited Destroy;
if Assigned(FStatisticsLock) then
FreeAndNil(FStatisticsLock);
if Assigned(FSourceFile) then
FreeAndNil(FSourceFile);
end;
procedure TFileSourceSplitOperation.UpdateStatistics(var NewStatistics: TFileSourceSplitOperationStatistics);
begin
FStatisticsLock.Acquire;
try
// Check if the value by which we calculate progress and remaining time has changed.
if FStatistics.DoneBytes <> NewStatistics.DoneBytes then
begin
with NewStatistics do
begin
RemainingTime :=
EstimateRemainingTime(FStatisticsAtStartTime.DoneBytes,
DoneBytes,
TotalBytes,
StartTime,
SysUtils.Now,
BytesPerSecond);
// Update overall progress.
if TotalBytes <> 0 then
UpdateProgress((DoneBytes * 100) div TotalBytes);
end;
end;
FStatistics := NewStatistics;
finally
FStatisticsLock.Release;
end;
end;
procedure TFileSourceSplitOperation.UpdateStatisticsAtStartTime;
begin
FStatisticsLock.Acquire;
try
Self.FStatisticsAtStartTime := Self.FStatistics;
finally
FStatisticsLock.Release;
end;
end;
function TFileSourceSplitOperation.RetrieveStatistics: TFileSourceSplitOperationStatistics;
begin
// Statistics have to be synchronized because there are multiple values
// and they all have to be consistent at every moment.
FStatisticsLock.Acquire;
try
Result := Self.FStatistics;
finally
FStatisticsLock.Release;
end;
end;
function TFileSourceSplitOperation.GetID: TFileSourceOperationType;
begin
Result := fsoSplit;
end;
procedure TFileSourceSplitOperation.DoReloadFileSources;
var
Paths: TPathsArray;
begin
SetLength(Paths, 1);
Paths[0] := FTargetPath; // Split target path
FFileSource.Reload(Paths);
end;
end.

View file

@ -86,6 +86,8 @@ type
TargetPath: String): TFileSourceOperation; override;
function CreateDeleteOperation(var FilesToDelete: TFiles): TFileSourceOperation; override;
function CreateWipeOperation(var FilesToWipe: TFiles): TFileSourceOperation; override;
function CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation;
function CreateCombineOperation(var SourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation; override;
function CreateCreateDirectoryOperation(BasePath: String; DirectoryPath: String): TFileSourceOperation; override;
@ -123,6 +125,7 @@ uses
uFileSystemMoveOperation,
uFileSystemDeleteOperation,
uFileSystemWipeOperation,
uFileSystemSplitOperation,
uFileSystemCombineOperation,
uFileSystemCreateDirectoryOperation,
uFileSystemExecuteOperation,
@ -547,6 +550,7 @@ begin
fsoMove,
fsoDelete,
fsoWipe,
fsoSplit,
fsoCombine,
fsoCreateDirectory,
fsoCalcChecksum,
@ -691,6 +695,15 @@ begin
Result := TFileSystemWipeOperation.Create(TargetFileSource, FilesToWipe);
end;
function TFileSystemFileSource.CreateSplitOperation(var aSourceFile: TFile;
aTargetPath: String): TFileSourceOperation;
var
SourceFileSource: IFileSource;
begin
SourceFileSource := Self;
Result := TFileSystemSplitOperation.Create(SourceFileSource, aSourceFile, aTargetPath);
end;
function TFileSystemFileSource.CreateCombineOperation(var SourceFiles: TFiles;
aTargetFile: String): TFileSourceOperation;
var

View file

@ -0,0 +1,327 @@
unit uFileSystemSplitOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceSplitOperation,
uFileSource,
uFileSourceOperationOptions,
uFileSourceOperationUI,
uFile,
uGlobs, uLog, uClassesEx;
type
{ TFileSystemSplitOperation }
TFileSystemSplitOperation = class(TFileSourceSplitOperation)
private
FStatistics: TFileSourceSplitOperationStatistics; // local copy of statistics
FTargetFile: UTF8String;
FBuffer: Pointer;
FBufferSize: LongWord;
FCheckFreeSpace: Boolean;
protected
function Split(aSourceFileStream: TFileStreamEx; TargetFile: UTF8String): Boolean;
procedure ShowError(sMessage: String);
procedure LogMessage(sMessage: String; logOptions: TLogOptions; logMsgType: TLogMsgType);
public
constructor Create(aFileSource: IFileSource;
var aSourceFile: TFile;
aTargetPath: String); override;
destructor Destroy; override;
procedure Initialize; override;
procedure MainExecute; override;
procedure Finalize; override;
end;
implementation
uses
uOSUtils, uLng, uFindEx, uFileSystemUtil, LCLProc, uTypes;
constructor TFileSystemSplitOperation.Create(aFileSource: IFileSource;
var aSourceFile: TFile;
aTargetPath: String);
begin
FCheckFreeSpace := True;
FTargetFile := aTargetPath + aSourceFile.Name;
FBufferSize := gCopyBlockSize;
GetMem(FBuffer, FBufferSize);
inherited Create(aFileSource, aSourceFile, aTargetPath);
end;
destructor TFileSystemSplitOperation.Destroy;
begin
inherited Destroy;
if Assigned(FBuffer) then
begin
FreeMem(FBuffer);
FBuffer := nil;
end;
end;
procedure TFileSystemSplitOperation.Initialize;
begin
// Get initialized statistics; then we change only what is needed.
FStatistics := RetrieveStatistics;
with FStatistics do
begin
CurrentFileFrom:= SourceFile.FullPath;
TotalFiles:= VolumeNumber;
TotalBytes:= SourceFile.Size;
end;
end;
procedure TFileSystemSplitOperation.MainExecute;
var
iExt, CurrentFileIndex: Integer;
iTotalDiskSize, iFreeDiskSize: Int64;
SourceFileStream: TFileStreamEx = nil;
TargetFile: UTF8String;
begin
try
{ Check disk free space }
if FCheckFreeSpace = True then
begin
GetDiskFreeSpace(TargetPath, iFreeDiskSize, iTotalDiskSize);
if FStatistics.TotalBytes > iFreeDiskSize then
begin
AskQuestion('', rsMsgNoFreeSpaceCont, [fsourAbort], fsourAbort, fsourAbort);
RaiseAbortOperation;
end;
end;
// Open source file
SourceFileStream := TFileStreamEx.Create(SourceFile.FullPath, fmOpenRead or fmShareDenyNone);
try
// Calculate extension length
iExt:= 2; // Minimum length 3 symbols
CurrentFileIndex:= FStatistics.TotalFiles;
while CurrentFileIndex >= 1 do
begin
CurrentFileIndex:= CurrentFileIndex div 1000;
Inc(iExt);
end;
for CurrentFileIndex := 1 to FStatistics.TotalFiles do
begin
TargetFile:= FTargetFile + ExtensionSeparator + Format('%.*d',[iExt, CurrentFileIndex]);
with FStatistics do
begin
// Last file can be smaller then volume size
if (TotalBytes - DoneBytes) < VolumeSize then
VolumeSize:= TotalBytes - DoneBytes;
CurrentFileTo := TargetFile;
CurrentFileTotalBytes := VolumeSize;
CurrentFileDoneBytes := 0;
end;
UpdateStatistics(FStatistics);
// Split with current file
if not Split(SourceFileStream, TargetFile) then Break;
with FStatistics do
begin
DoneFiles := DoneFiles + 1;
UpdateStatistics(FStatistics);
end;
CheckOperationState;
end;
finally
if Assigned(SourceFileStream) then
begin
FreeAndNil(SourceFileStream);
if (FStatistics.DoneBytes <> FStatistics.TotalBytes) then
begin
for CurrentFileIndex := 1 to FStatistics.TotalFiles do
// There was some error, because not all files has been created.
// Delete the not completed target files.
mbDeleteFile(FTargetFile + ExtensionSeparator + Format('%.*d',[iExt, CurrentFileIndex]));
end;
end;
end;
except
on EFOpenError do
begin
ShowError(rsMsgLogError + rsMsgErrEOpen + ': ' + SourceFile.FullPath);
end;
end;
end;
procedure TFileSystemSplitOperation.Finalize;
begin
end;
function TFileSystemSplitOperation.Split(aSourceFileStream: TFileStreamEx; TargetFile: UTF8String): Boolean;
var
TargetFileStream: TFileStreamEx = nil; // for safety exception handling
iTotalDiskSize, iFreeDiskSize: Int64;
bRetryRead, bRetryWrite: Boolean;
BytesRead, BytesToRead, BytesWrittenTry, BytesWritten: Int64;
TotalBytesToRead: Int64 = 0;
begin
Result := False;
BytesToRead := FBufferSize;
try
try
TargetFileStream := TFileStreamEx.Create(TargetFile, fmCreate);
TotalBytesToRead := VolumeSize;
while TotalBytesToRead > 0 do
begin
// Without the following line the reading is very slow
// if it tries to read past end of file.
if TotalBytesToRead < BytesToRead then
BytesToRead := TotalBytesToRead;
repeat
try
bRetryRead := False;
BytesRead := aSourceFileStream.Read(FBuffer^, BytesToRead);
if (BytesRead = 0) then
Raise EReadError.Create(mbSysErrorMessage(GetLastOSError));
TotalBytesToRead := TotalBytesToRead - BytesRead;
BytesWritten := 0;
repeat
try
bRetryWrite := False;
BytesWrittenTry := TargetFileStream.Write((FBuffer + BytesWritten)^, BytesRead);
BytesWritten := BytesWritten + BytesWrittenTry;
if BytesWrittenTry = 0 then
begin
Raise EWriteError.Create(mbSysErrorMessage(GetLastOSError));
end
else if BytesWritten < BytesRead then
begin
bRetryWrite := True; // repeat and try to write the rest
end;
except
on E: EWriteError do
begin
{ Check disk free space }
GetDiskFreeSpace(TargetPath, iFreeDiskSize, iTotalDiskSize);
if BytesRead > iFreeDiskSize then
begin
case AskQuestion(rsMsgNoFreeSpaceRetry, '',
[fsourYes, fsourNo],
fsourYes, fsourNo) of
fsourYes:
bRetryWrite := True;
fsourNo:
RaiseAbortOperation;
end; // case
end
else
begin
case AskQuestion(rsMsgErrEWrite + ' ' + TargetFile + ':',
E.Message,
[fsourRetry, fsourSkip, fsourAbort],
fsourRetry, fsourSkip) of
fsourRetry:
bRetryWrite := True;
fsourAbort:
RaiseAbortOperation;
fsourSkip:
Exit;
end; // case
end;
end; // on do
end; // except
until not bRetryWrite;
except
on E: EReadError do
begin
case AskQuestion(rsMsgErrERead + ' ' + SourceFile.FullPath + ':',
E.Message,
[fsourRetry, fsourSkip, fsourAbort],
fsourRetry, fsourSkip) of
fsourRetry:
bRetryRead := True;
fsourAbort:
RaiseAbortOperation;
fsourSkip:
Exit;
end; // case
end;
end;
until not bRetryRead;
with FStatistics do
begin
CurrentFileDoneBytes := CurrentFileDoneBytes + BytesRead;
DoneBytes := DoneBytes + BytesRead;
UpdateStatistics(FStatistics);
end;
CheckOperationState; // check pause and stop
end; //while
finally
if Assigned(TargetFileStream) then
FreeAndNil(TargetFileStream);
end;
Result:= True;
except
on EFCreateError do
begin
ShowError(rsMsgLogError + rsMsgErrECreate + ': ' + TargetFile);
end;
on EWriteError do
begin
ShowError(rsMsgLogError + rsMsgErrEWrite + ': ' + TargetFile);
end;
end;
end;
procedure TFileSystemSplitOperation.ShowError(sMessage: String);
begin
if gSkipFileOpError then
logWrite(Thread, sMessage, lmtError, True)
else
begin
AskQuestion(sMessage, '', [fsourAbort], fsourAbort, fsourAbort);
RaiseAbortOperation;
end;
end;
procedure TFileSystemSplitOperation.LogMessage(sMessage: String; logOptions: TLogOptions; logMsgType: TLogMsgType);
begin
case logMsgType of
lmtError:
if not (log_errors in gLogOptions) then Exit;
lmtInfo:
if not (log_info in gLogOptions) then Exit;
lmtSuccess:
if not (log_success in gLogOptions) then Exit;
end;
if logOptions <= gLogOptions then
begin
logWrite(Thread, sMessage, logMsgType);
end;
end;
end.

View file

@ -2545,7 +2545,6 @@ end;
procedure TActs.cm_FileLinker(param:string);
var
I: Integer;
Result: Boolean;
aSelectedFiles: TFiles = nil;
aFile: TFile;
begin
@ -2558,7 +2557,6 @@ begin
end;
try
Result:= False;
aSelectedFiles := CloneSelectedFiles;
for I := 0 to aSelectedFiles.Count - 1 do
@ -2583,42 +2581,26 @@ end;
procedure TActs.cm_FileSpliter(param:string);
var
sl: TStringList = nil;
I: Integer;
Result: Boolean;
aSelectedFiles: TFiles = nil;
aFile: TFile;
aFile: TFile = nil;
begin
with frmMain, frmMain.ActiveFrame do
begin
// For now only works for FileSystem.
if FileSource.IsClass(TFileSystemFileSource) then
begin
sl:= TStringList.Create;
try
Result:= False;
aSelectedFiles := CloneSelectedFiles;
if not (fsoSplit in FileSource.GetOperationsTypes) then
begin
msgWarning(rsMsgErrNotSupported);
Exit;
end;
for I := 0 to aSelectedFiles.Count - 1 do
begin
aFile := aSelectedFiles[I];
if not (aFile.IsDirectory or aFile.IsLinkToDirectory) then
sl.Add(CurrentPath + aFile.Name);
end;
if sl.Count > 0 then
Result:= ShowSplitterFileForm(sl, NotActiveFrame.CurrentPath);
finally
FreeThenNil(sl);
FreeThenNil(aSelectedFiles);
if Result then
begin
ActiveFrame.Reload;
NotActiveFrame.Reload;
end;
ActiveFrame.SetFocus;
end; // try
end; // if
try
aFile := CloneActiveFile;
if (not Assigned(aFile)) or (aFile.IsDirectory or aFile.IsLinkToDirectory) then
msgWarning(rsMsgInvalidSelection)
else
ShowSplitterFileForm(FileSource, aFile, NotActiveFrame.CurrentPath);
finally
FreeThenNil(aFile);
end; // try
end; // with
end;

View file

@ -180,7 +180,8 @@ resourcestring
rsDlgSpeedTime = 'Speed %s/s, time remaining %s';
rsDlgDel = 'Delete file(s)';
rsDlgWipe = 'Wipe file(s)';
rsDlgCombine = 'Combine files';
rsDlgSplit = 'Split';
rsDlgCombine = 'Combine';
rsDlgCp = 'Copy file(s)';
rsDlgMv = 'Move file(s)';
rsDlgPack = 'Packing...';
@ -278,10 +279,7 @@ resourcestring
rsSplitErrFileSize = 'Incorrect file size format!';
rsSplitErrDirectory = 'Unable to create target directory!';
rsSplitErrSplitFile = 'Unable to split the file!';
rsSplitErrTooManyParts = 'Too many parts!';
rsSplitMsgManyParts = 'The number of parts is more than 100! Continue?';
rsSplitMsgCreated = 'Created:';
rsSplitMsgSize = 'size:';
// MultiRename dialog
rsMulRenFileNameStyleList = 'No change;UPPERCASE;lowercase;First char uppercase;' +
'First Char Of Every Word Uppercase;';