Mega Code Archive

 
Categories / Delphi / Examples
 

Zipping Splitting and Sending PART I

Title: Zipping Splitting and Sending PART I Question: How to split a file. Answer: Splitting files PART 1 Zipping, Splitting and Sending Why would we want to split files? Lets say you want to send new application updates too your clients, but the file size is greater than what the clients mailbox permits. So the solution wont it be great if I zipped the file, and then check the size, if its still too large I will split the zipped file, into parts, with sizes I can define myself.yup sounds good lets do that. What youll need for my article 1. A zip application, Ive catered for WinZip, and FilZip in my code, but if you have another zip application, and know its exported functions, just replace either WinZip or FilZip code with yours. 2. The Indy clients components I only used the components for the actual emailing facility. You can use any method you want. START HERE 1.Create new Application, rename the form frmMain, and caption Split & Join Demo. Drop a PageControl component onto your form rename it pgcMain, align all client. Create two tabsheets. Change the first tabsheets caption too Zip, split and send and name to tbsSplitSend. The others caption Join and Unzip and name tbsUnzipJoin. 2.On tbsSplitSend - Drop a TLabel, TEdit, TOpendialog, and TSpeedbutton component. Rename them too lblFileName, edtFilename, dlgOpenfile, and btnOpenFile. 3.Drop another TLabel rename it lblFileSize, and caption too 0. 4.SORRY ALMOST DONE - Drop six more TEdits, align them nicely. Rename them edtHost This will be your email servers ipaddress. edtName This will be the senders name, i.e. dappie@webmail.co.za. edtRecipient This will be the email recipients (; separated if sent too more recipients). edtSubject This will be the emailssubject. edtUpdateDir The files will be zipped, joined and sent from this folder. edtZipDir Location where the .exe file for the zip application you want to use is located.Normally c:\Program Files\WinZip\.PLEASE NOTE THAT THE ZIP/UPDATE PATHS SHOULD END WITH A \ CHARACTER.Drop six TLabel components and align them nicely. 5.Drop a TMemo component. Rename it memEmailBody This will be your actual email message body. Again drop a TLabel component and align them nicely. 6.Change all label captions, so that they would make sense too you, or the user. 7.Finally drop another TSpeedbutton, and rename it btnProcessEmail. Remember to add glyphs too your speedbuttons, if needed. 8.Add a datamodule to your application rename it dtmMain. Drop a TIdSMTP component, found in the Indy clients component tab. Change the creation order of the application, move the datamodule so that it will be created first. Can be found at Project - Options - Forms tab. 9.Create a new unit uConst. All your constant values will be held here. Some variables aswell, - please note that storing variables in this manner is not best practice, one should rather make use of properties. Copy the following code for uConst. ------------------------------- UCONST START ----------------------------------- unit uConst; interface uses Classes, WinTypes; var ZipType : integer; ZipApp : string; const INITOUSE = 'SplitDemo.ini'; WINZIP = 0; FILZIP = 1; MAXFILESIZE = 900; ZERO = 0; ZIPFILE = 0; UNZIPFILE = 1; Table : array[0..255] of DWord = ($00000000, $77073096, $EE0E612C, $990951BA, $076DC419, $706AF48F, $E963A535, $9E6495A3, $0EDB8832, $79DCB8A4, $E0D5E91E, $97D2D988, $09B64C2B, $7EB17CBD, $E7B82D07, $90BF1D91, $1DB71064, $6AB020F2, $F3B97148, $84BE41DE, $1ADAD47D, $6DDDE4EB, $F4D4B551, $83D385C7, $136C9856, $646BA8C0, $FD62F97A, $8A65C9EC, $14015C4F, $63066CD9, $FA0F3D63, $8D080DF5, $3B6E20C8, $4C69105E, $D56041E4, $A2677172, $3C03E4D1, $4B04D447, $D20D85FD, $A50AB56B, $35B5A8FA, $42B2986C, $DBBBC9D6, $ACBCF940, $32D86CE3, $45DF5C75, $DCD60DCF, $ABD13D59, $26D930AC, $51DE003A, $C8D75180, $BFD06116, $21B4F4B5, $56B3C423, $CFBA9599, $B8BDA50F, $2802B89E, $5F058808, $C60CD9B2, $B10BE924, $2F6F7C87, $58684C11, $C1611DAB, $B6662D3D, $76DC4190, $01DB7106, $98D220BC, $EFD5102A, $71B18589, $06B6B51F, $9FBFE4A5, $E8B8D433, $7807C9A2, $0F00F934, $9609A88E, $E10E9818, $7F6A0DBB, $086D3D2D, $91646C97, $E6635C01, $6B6B51F4, $1C6C6162, $856530D8, $F262004E, $6C0695ED, $1B01A57B, $8208F4C1, $F50FC457, $65B0D9C6, $12B7E950, $8BBEB8EA, $FCB9887C, $62DD1DDF, $15DA2D49, $8CD37CF3, $FBD44C65, $4DB26158, $3AB551CE, $A3BC0074, $D4BB30E2, $4ADFA541, $3DD895D7, $A4D1C46D, $D3D6F4FB, $4369E96A, $346ED9FC, $AD678846, $DA60B8D0, $44042D73, $33031DE5, $AA0A4C5F, $DD0D7CC9, $5005713C, $270241AA, $BE0B1010, $C90C2086, $5768B525, $206F85B3, $B966D409, $CE61E49F, $5EDEF90E, $29D9C998, $B0D09822, $C7D7A8B4, $59B33D17, $2EB40D81, $B7BD5C3B, $C0BA6CAD, $EDB88320, $9ABFB3B6, $03B6E20C, $74B1D29A, $EAD54739, $9DD277AF, $04DB2615, $73DC1683, $E3630B12, $94643B84, $0D6D6A3E, $7A6A5AA8, $E40ECF0B, $9309FF9D, $0A00AE27, $7D079EB1, $F00F9344, $8708A3D2, $1E01F268, $6906C2FE, $F762575D, $806567CB, $196C3671, $6E6B06E7, $FED41B76, $89D32BE0, $10DA7A5A, $67DD4ACC, $F9B9DF6F, $8EBEEFF9, $17B7BE43, $60B08ED5, $D6D6A3E8, $A1D1937E, $38D8C2C4, $4FDFF252, $D1BB67F1, $A6BC5767, $3FB506DD, $48B2364B, $D80D2BDA, $AF0A1B4C, $36034AF6, $41047A60, $DF60EFC3, $A867DF55, $316E8EEF, $4669BE79, $CB61B38C, $BC66831A, $256FD2A0, $5268E236, $CC0C7795, $BB0B4703, $220216B9, $5505262F, $C5BA3BBE, $B2BD0B28, $2BB45A92, $5CB36A04, $C2D7FFA7, $B5D0CF31, $2CD99E8B, $5BDEAE1D, $9B64C2B0, $EC63F226, $756AA39C, $026D930A, $9C0906A9, $EB0E363F, $72076785, $05005713, $95BF4A82, $E2B87A14, $7BB12BAE, $0CB61B38, $92D28E9B, $E5D5BE0D, $7CDCEFB7, $0BDBDF21, $86D3D2D4, $F1D4E242, $68DDB3F8, $1FDA836E, $81BE16CD, $F6B9265B, $6FB077E1, $18B74777, $88085AE6, $FF0F6A70, $66063BCA, $11010B5C, $8F659EFF, $F862AE69, $616BFFD3, $166CCF45, $A00AE278, $D70DD2EE, $4E048354, $3903B3C2, $A7672661, $D06016F7, $4969474D, $3E6E77DB, $AED16A4A, $D9D65ADC, $40DF0B66, $37D83BF0, $A9BCAE53, $DEBB9EC5, $47B2CF7F, $30B5FFE9, $BDBDF21C, $CABAC28A, $53B39330, $24B4A3A6, $BAD03605, $CDD70693, $54DE5729, $23D967BF, $B3667A2E, $C4614AB8, $5D681B02, $2A6F2B94, $B40BBE37, $C30C8EA1, $5A05DF1B, $2D02EF8D); type THeaderBlock = record ProgramName : string[30]; Fill1 : char; OriginalFile : string[30]; Fill2 : char; end; implementation end. -------------------------------- UCONST END ------------------------------------ 10.Create a new unit uBase. All your global functions & procedures will reside here, simple reasons - reuse of procedures, simplifies maintenance on your application, and speeds up the actual development time. Copy the following code for uBase. ------------------------------- UBASE START ------------------------------------ unit uBase; interface uses Windows, IniFiles, Registry, SysUtils, Controls, Dialogs, uConst, ShellApi, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdMessageClient, IdSMTP, IdTCPServer, IdSMTPServer, IdMessage, Classes; var EmailMessage : TIdMessage; function MesDlg(sMessage :shortstring; iWhat :char) : TModalResult; function cCopyFiles(inSource, inDestination : string) : boolean; function ReadIni(sIniName, sSection, sIDent : shortstring) : shortstring; procedure WriteIni(sIniName, sSection, sIDent, sToWrite : shortstring); procedure RegisterFiletype(FileType,FileKey,Description,Icon,ProgramUsed : string); function GetSize(inFilename : string) : integer; procedure DoZipProcedure(FromFile,ToFile : shortString ; inProcess : integer); procedure SplitFile(inSize : integer; inFile : string); function GetChecksum(FileHandle : THandle) : DWord; function CalcCRC32(inPointer : Pointer; ByteCount : DWord) : DWord; procedure SendEmail(inZipFile, EmailHost, EmailName, Recipient , Subject : string; Body : TStrings); procedure AddAttachments(inFile : string); function BuildUpdateFile(inFileName,CurrFolder,FileNumber : string) : string; function GetNewFileName(inFileName,inFolder,inFileNumber : shortstring) : shortstring; implementation uses dmMain; {******************************* GENERAL START ********************************} function MesDlg(sMessage :shortstring; iWhat :char):TModalResult; begin case iWhat of 'E' : Result := MessageDlg(sMessage,mtError,[mbOk],ZERO); 'F' : Result := MessageDlg(sMessage,mtConfirmation,mbYesNoCancel,ZERO); 'I' : Result := MessageDlg(sMessage,mtInformation,[mbOk],ZERO); 'C' : Result := MessageDlg(sMessage,mtConfirmation,[mbYes,mbNo],ZERO); 'W' : Result := MessageDlg(sMessage,mtWarning,[mbOk],ZERO); 'X' : Result := MessageDlg(sMessage,mtWarning,[mbYes,mbNo],ZERO); else Result := mrNone; end; end; function cCopyFiles(inSource, inDestination : string) : boolean; begin //Overwrite file Result := CopyFile(pChar(inSource),pChar(inDestination),false); end; {******************************** GENERAL END *********************************} {****************************** INI FILES START *******************************} function ReadIni(sIniName, sSection, sIDent: shortstring): shortstring; var IniFileName : TIniFile; begin IniFileName := TIniFile.Create(sIniName); Result := IniFileName.ReadString(sSection,sIDent,''); IniFileName.Free; end; procedure WriteIni(sIniName, sSection, sIDent, sToWrite: shortstring); var IniFileName : TIniFile; begin IniFileName := TIniFile.Create(sIniName); IniFileName.WriteString(sSection,sIDent,sToWrite); IniFileName.Free; end; {******************************* INI FILES END ********************************} {****************************** REGISTRY START ********************************} procedure RegisterFiletype(FileType,FileKey,Description,Icon,ProgramUsed : string); var Reg : TRegInifile; Count : integer; begin Count := pos('.',FileType); while Count 0 do begin Delete(FileType,Count,1); Count := pos('.',FileType); end; if (FileType = '') or (ProgramUsed = '') then exit; Reg := TRegInifile.Create(''); try Reg.RootKey := HKEY_CLASSES_ROOT;//Where all file-types are described if FileKey = '' then FileKey := Copy(FileType,2,MaxInt)+'_auto_file';//if no key-name is given,create one Reg.WriteString(FileType,'',FileKey); //set a pointer to the description-key Reg.WriteString(FileKey,'',Description); //write the description if Icon '' then Reg.WriteString(FileKey+'\DefaultIcon','',Icon);//Write the def-icon if given Reg.WriteString(FileKey+'\shell\open\command','',ProgramUsed+' "%1"');//association finally Reg.free; end; end; {******************************* REGISTRY END *********************************} {***************************** SIZE FILES START *******************************} function GetSize(inFilename : string) : integer; var Handle1 : THandle; Win32FData : TWIN32FINDDATA; begin Result := 0; if not FileExists(inFilename) then Exit; Handle1 := FindFirstFile(pchar(inFilename),Win32FData); if Handle1 = INVALID_HANDLE_VALUE then Exit; Result := Win32FData.nFileSizeLow; Windows.FindClose(Handle1); end; {FILZIP} { -a : Add files -e : Extract files ADD r : Add recursive p : Save path names f : Save full path info s : Load filelist from parameter file EXTRACT r : restore path names o : overwrite existing files u : update older files {******************************************************************************} {******************************************************************************} {WINZIP} {-min : Run WinZip as minimized. -a : Add files. -s : Encript with password. -e : Extract files. -o & -j: Overwrite existing files without prompting. {******************************************************************************} {This procedure is used when ever files need zip, or unzip processes The ZipType is set after the GetZipApplication on the main form was completed WINZIP and FILZIP is defined within the uConst unit, the process defines which task should be carried out, either zip or unzip} procedure DoZipProcedure(FromFile,ToFile : shortString ; inProcess : integer); var ExecPath : string; begin case ZipType of WINZIP : begin case inProcess of ZIPFILE : ExecPath := ZipApp+' -min -a '+ToFile+' '+FromFile; UNZIPFILE : ExecPath := ZipApp+' -min -e -o & -j '+FromFile+' '+ToFile; end; end; FILZIP : begin case inProcess of ZIPFILE : ExecPath := ZipApp+' -a -r '+ToFile+' '+FromFile; UNZIPFILE : ExecPath := ZipApp+' -e -o '+FromFile+' '+ToFile; end; end; end; WinExec(PChar(ExecPath),SW_HIDE); end; procedure SplitFile(inSize : integer; inFile : string); var Count : integer; ReadSize : integer; Buffer : pchar; ReadHandle : THandle; WriteHandle : THandle; OldCursor : HCursor; CheckSum : DWord; SplitFileName : string; Modified : TFileTime; begin GetMem(Buffer,inSize); ReadHandle := FileOpen(inFile,fmOpenRead or fmShareDenyNone); OldCursor := SetCursor(LoadCursor(0,IDC_WAIT)); Checksum := GetChecksum(ReadHandle); WriteHandle := FileCreate(inFile+'.999'); FileWrite(WriteHandle,Checksum,SizeOf(Checksum)); FileClose(WriteHandle); GetFileTime(ReadHandle,nil,nil,@Modified); Count := 1; repeat ReadSize := FileRead(ReadHandle,Buffer^,inSize); SplitFilename := inFile+Format('.%3.3d',[Count]); WriteHandle := FileCreate(SplitFilename); FileWrite(WriteHandle,Buffer^,ReadSize); SetFileTime(WriteHandle,nil,nil,@Modified); FileClose(WriteHandle); inc(Count); until (ReadSize inSize); FileClose(ReadHandle); SetCursor(OldCursor); FreeMem(Buffer); end; function GetChecksum(FileHandle : THandle) : DWord; var MapHandle : THandle; MemPointer : Pointer; begin Result := $FFFFFFFF; MemPointer := nil; MapHandle := 0; try MapHandle := CreateFileMapping(FileHandle,nil,PAGE_READONLY,0,0,nil); if MapHandle = 0 then Exit; MemPointer := MapViewOfFile(MapHandle,FILE_MAP_READ,0,0,0); if MemPointer = nil then Exit; Result := CalcCRC32(MemPointer,GetFileSize(FileHandle,nil)); finally if Assigned(MemPointer) then UnmapViewOfFile(MemPointer); if MapHandle 0 then CloseHandle(MapHandle); end; end; function CalcCRC32(inPointer : Pointer; ByteCount : DWord) : DWord; var i : DWord; q : PByte; begin q := inPointer; Result := $FFFFFFFF; for i := 0 to ByteCount-1 do begin Result := (Result shr 8) xor Table[q^ xor (Result and $000000ff)]; inc(q); end; Result := not Result; end; {******************************** SIZE FILES END ******************************} {****************************** EMAIL FILES START *****************************} procedure SendEmail(inZipFile, EmailHost, EmailName, Recipient , Subject : string; Body : TStrings); var i : integer; begin dtmDemo.SMTPHost.Host := EmailHost; if dtmDemo.SMTPHost.Connected = False then dtmDemo.SMTPHost.Connect; EmailMessage := TIdMessage.Create(nil); EmailMessage.From.Name := EmailName; EmailMessage.Recipients.EMailAddresses := Recipient; EmailMessage.Subject := Subject; EmailMessage.Body.Clear; for i := 0 to Body.Count-1 do EmailMessage.Body.Add(Body.Strings[i]); AddAttachments(inZipFile); EmailMessage.Free; EmailMessage := nil; dtmDemo.SMTPHost.Disconnect; end; procedure AddAttachments(inFile : string); var CurrFolder : string; SplitFile : string; SplitPart : string; i : integer; CurrNo : string; FileAttach : TIdAttachment; begin CurrFolder := ExtractFilePath(inFile); SplitFile := inFile+'.999'; if FileExists(SplitFile) then begin for i := 1 to 999 do begin CurrNo := IntToStr(i); while length(CurrNo) CurrNo := '0'+CurrNo; SplitPart := inFile+'.'+CurrNo; if FileExists(SplitPart) then begin FileAttach := TIdAttachment.Create(EmailMessage.MessageParts,BuildUpdateFile(SplitPart,CurrFolder,CurrNo)); dtmDemo.SMTPHost.Send(EmailMessage); FileAttach.Free; end; end; end else begin FileAttach := TIdAttachment.Create(EmailMessage.MessageParts,BuildUpdateFile(ExtractFileName(inFile),CurrFolder,CurrNo)); dtmDemo.SMTPHost.Send(EmailMessage); FileAttach.Free; end; end; function BuildUpdateFile(inFileName,CurrFolder,FileNumber : string) : string; var SaveAsName : string; FileName : shortstring; HeaderBlock : THeaderBlock; UpdateFile : File; CurrFile : TFileStream; NewFile : TFileStream; My2ByteBuffer : array[1..1024] of char; begin SaveAsName := inFileName; Result := GetNewFileName(inFileName,CurrFolder,FileNumber); {$I+} if not FileExists(Result) then cCopyFiles(inFileName,Result); FileName := Result+Chr(0); AssignFile(UpdateFile,FileName); Rewrite(UpdateFile,1); FillChar(HeaderBlock,SizeOf(THeaderBlock),0); HeaderBlock.ProgramName := trim('AutoUpdater.exe'); HeaderBlock.OriginalFile := SaveAsName; FillChar(My2ByteBuffer,SizeOf(My2ByteBuffer),0); Move(HeaderBlock,My2ByteBuffer[1],SizeOf(THeaderBlock)); BlockWrite(UpdateFile,My2ByteBuffer,1024); CloseFile(UpdateFile); CurrFile := TFileStream.Create(inFileName,fmOpenRead); CurrFile.Seek(0,soFromBeginning); NewFile := TFileStream.Create(FileName,fmOpenWrite); NewFile.Seek(0,soFromEnd); NewFile.CopyFrom(CurrFile,CurrFile.Size); CurrFile.Free; NewFile.Free; {$I-} end; function GetNewFileName(inFileName,inFolder,inFileNumber : shortstring) : shortstring; var NewName : string; begin NewName := Copy(inFileName,0,pos('.',inFileName)-1); if pos('.zip.',inFileName) 0 then begin if inFileNumber = '999' then Result := NewName+'_link.dap' else Result := NewName+'_'+inFileNumber+'.dap'; end else Result := inFileName; end; {******************************* EMAIL FILES END ******************************} end. ------------------------------ UBASE END --------------------------------------- HARD WORK DONE CODING STARTS HERE 1.On frmMains form show event. procedure TfrmMain.FormShow(Sender : TObject); begin pgcMain.ActivePageIndex := 0; //Set the tbsSplitSend as the active tabsheet. pgcMain.OnChange(nil); end; 2.On pgcMains on change event. procedure TfrmMain.pgcDemoChange(Sender: TObject); begin case pgcMain.ActivePageIndex of 0 : begin // tabsheet number 0 tbsSplitSend {Get default settings read it from a ini file, "INITOUSE" which is a constant in the Uconst unit. Use the ReadIni wrapper procedure found in the uBase unit.} edtHost.Text := ReadIni(INITOUSE,'EmailSettings','EmailHost');//Email host server ip. edtName.Text := ReadIni(INITOUSE,'EmailSettings','EmailName');//The display name, as seen by the email recipient. edtSubject.Text := ReadIni(INITOUSE,'EmailSettings','EmailSubject');//Email's subject. memMessageBody.Lines.Text := ReadIni(INITOUSE,'EmailSettings','EmailBody');//Email message. edtUpdateDir.Text := ReadIni(INITOUSE,'Updates','UpdateDirectory');//Update directory, directory that will be used to work with the files you need to send. edtZipDir.Text := ReadIni(INITOUSE,'Updates','ZipDirectory');//Update directory, directory that will be used to work with the files you need to send. if length(edtUpdateDir.Text) 0 then if not DirectoryExists(edtUpdateDir.Text) then MesDlg('Please provide a existing update directory, specified directory does not exist.','E'); {Get's the zip application that is going to be used to process the files, t he application's executable must be within the Zip directory} if FindFirst(edtZipDir.Text+'winzip32.exe',faAnyFile,FindFile) = 0 then begin ZipApp := edtZipDir.Text+'winzip32.exe'; ZipType := WINZIP; end; if FindFirst(edtZipDir.Text+'Filzip.exe',faAnyFile,FindFile) = 0 then begin ZipApp := edtZipDir.Text+'Filzip.exe'; ZipType := FILZIP; end; end; 1 : RegisterFileType('.dap','DemoSplitJoin','Split and Join zip file',ExtractFilePath(Application.ExeName)+'Demo.ico',Application.ExeName); end; end; 3.On the btnFileNames click event procedure TfrmMain.btnFilenameClick(Sender: TObject); begin if dlgOpenFile.Execute then begin Filename := dlgOpenFile.FileName; edtFilename.Text := Filename; FileSize := GetSize(Filename); lblFileSize.Caption := 'File size : '+FloatToStrF((FileSize/1024),ffNumber,9,2); end; end; 4.On btnProcessEmails click event procedure TfrmMain.btnProcessEmailClick(Sender: TObject); begin if length(edtRecipient.Text) 0 then begin Self.Cursor := crHourGlass; GetZipFileName; SaveSettingsToIni; if length(edtUpdateDir.Text) 0 then if not DirectoryExists(edtUpdateDir.Text) then MesDlg('Please provide a existing update directory, specified directory does not exist.','E') else begin if length(ExtractFileName(Filename)) 0 then begin DoZipProcedure(FileName,ZipFileName,ZIPFILE); while not FileExists(ZipFileName) do Sleep(0); FileSize := GetSize(ZipFileName); if (FileSize/1024) MAXFILESIZE then begin SplitSize := MAXFILESIZE*1024; SplitFile(SplitSize,ZipFileName); end; SendEmail(ZipFileName,edtHost.Text,edtName.Text,edtRecipient.Text,edtSubject.Text,memMessageBody.Lines); end else begin MesDlg('Please provide a file you would like to email.','E'); edtFilename.SetFocus; end; end; Self.Cursor := crDefault; end else MesDlg('Please add your recipients.','E'); end; 5.Finally create two new private procedures, and add private variables ....private { Private declarations } Filename : string; ZipFileName : string; FileSize : integer; SplitSize : integer; FindFile : TSearchRec; procedure GetZipFileName; procedure SaveSettingsToIni;.... procedure TfrmMain.GetZipFileName; begin ZipFileName := Copy(ExtractFileName(Filename),0,pos('.',ExtractFileName(Filename))); ZipFileName := edtUpdateDir.Text+ZipFileName+'zip'; end; procedure TfrmMain.SaveSettingsToIni; begin WriteIni(INITOUSE,'EmailSettings','EmailHost',edtHost.Text); WriteIni(INITOUSE,'EmailSettings','EmailName',edtName.Text); WriteIni(INITOUSE,'EmailSettings','EmailSubject',edtSubject.Text); WriteIni(INITOUSE,'EmailSettings','EmailBody',memMessageBody.Text); WriteIni(INITOUSE,'Updates','UpdateDirectory',edtUpdateDir.Text); WriteIni(INITOUSE,'Updates','ZipDirectory',edtZipDir.Text); end; by Dewald Marais Part II coming soon.