Matthew Hipkin

Base64 encode/decode binary files with Lazarus

Download: https://sourceforge.net/projects/base64-binary/

FreePascal already comes with all the objects necessary to bas64 encode and decode binary files. All you need is to add the base64 unit to your uses clause.

To encode binary files you'll need to use the TBase64EncodingStream object.

procedure EncodeFile(inputfile: String; outputfile: String);
var
  fi,fo: TFileStream;
  enc: TBase64EncodingStream;
  newof: String;
begin
  if Length(outputfile) < 1 then
  begin
    if Length(ExtractFileExt(inputfile)) > 0 then
      newof := AnsiReplaceStr(inputfile, ExtractFileExt(inputfile), '.txt')
    else newof := inputfile + '.txt';
  end
  else newof := outputfile;
  write('Reading ',inputfile);
  fi := TFileStream.Create(inputfile,fmOpenRead);
  writeln('... Done');
  write('Writing ',newof);
  fo := TFileStream.Create(newof,fmCreate);
  enc := TBase64EncodingStream.Create(fo);
  enc.CopyFrom(fi,fi.Size);
  enc.Free;
  writeln('... Done');
  fo.Free;
  fi.Free;
end;

We firstly read our input file into a standard TFileStream, pass it to the TBase64EncodingStream which automatically puts the encoded data into the specified output stream, which in this case is another TFileStream.

Decoding is achieved in a similar way.

procedure DecodeFile(inputfile: String; outputfile: String);
var
  fo: TFileStream;
  enc: TBase64DecodingStream;
  s: TStringStream;
  F: TextFile;
  si: String;
begin
  fo := TFileStream.Create(outputfile,fmCreate);
  write('Reading ',inputfile);
  AssignFile(F,inputfile);
  Reset(F);
  Read(F,si);
  CloseFile(F);
  writeln('... Done');
  s := TStringStream.Create(si);
  write('Writing ',outputfile);
  enc := TBase64DecodingStream.Create(s);
  fo.CopyFrom(enc,enc.Size);
  enc.Free;
  writeln('... Done');
  fo.Free;
end;

This time, we load our encoded file into a TStringStream, pass that to a TBase64DecodingStream which automatically puts it into the output stream which once again is a TFileStream.

If you're developing a GUI application using Lazarus and want to display a progress bar, instead of copying the whole stream in a single CopyFrom() call you can split it into chunks. An example of this could be:

procedure TfrmMain.EncodeFile;
const
  CHUNKSIZE = 524288; // Set a chunk size of 512k
var
  fi: TFileStream;
  fo: TFileStream;
  enc: TBase64EncodingStream;
begin
  // Setup streams
  fi := TFileStream.Create(textInputFile.FileName,fmOpenRead);
  fo := TFileStream.Create(textOutputFile.FileName,fmCreate or fmOpenWrite);
  enc := TBase64EncodingStream.Create(fo);
  // Reset input file stream position
  fi.Position := 0;
  // Set progress bar max value to filesize of input
  ProgressBar1.Max := FileSize(textInputFile.FileName);
  { Read the stream, looping in increments of CHUNKSIZE until stream is at
    an end }
  while fi.Position < fi.Size do
  begin
    if (fi.Position + CHUNKSIZE) < fi.Size then enc.CopyFrom(fi,CHUNKSIZE)
    else enc.CopyFrom(fi,fi.Size-fi.Position);
    // Set progress bar position
    ProgressBar1.Position := fi.Position;
    // Keep UI alive
    Application.ProcessMessages;
  end;
  enc.Free;
  fo.Free;
  fi.Free;
end;

You can download my base64 encoder/decoder created with these function (both console and GUI) from here.

blog comments powered by Disqus