Base64 to Binary (Delphi) - delphi

I used Binary to Base64 function that you answered :
Binary to Base64 (Delphi)
I successfully encode a file to base64 string and write it to MsSQL2008 database, but i want to ask a question:
How can i write this file to disk again with using EncdDecd.pas?

As always, David answered sufficiently. Although I can't resist to give a slightly different solution using some of the goodies from the recent Delphi versions.
procedure DecodeFile(const base64: AnsiString; const FileName: string);
var
stream: TBytesStream;
begin
stream := TBytesStream.Create(DecodeBase64(base64));
try
stream.SaveToFile(Filename);
finally
stream.Free;
end;
end;

This function will take a base64 encoded string, decode it, and write the resulting byte array to a file.
procedure DecodeToFile(const base64: AnsiString; const FileName: string);
var
stream: TFileStream;
bytes: TBytes;
begin
bytes := DecodeBase64(base64);
stream := TFileStream.Create(FileName, fmCreate);
try
if bytes<>nil then
stream.Write(bytes[0], Length(Bytes));
finally
stream.Free;
end;
end;
To explain what is happening here, the first line
bytes := DecodeBase64(base64);
performs the decode and returns the decoded binary contents of the file in a TBytes variable. TBytes is simply an array of bytes.
The next step is to create the file. The idiomatic way to write files in Delphi is to use streams. In this case we want a TFileStream.
stream := TFileStream.Create(FileName, fmCreate);
The fmCreate option means that if the file already exists, it will be replaced and overwritten by what we write.
The final step is to write the contents of the byte array to the file
if bytes<>nil then
stream.Write(bytes[0], Length(Bytes));
The if bytes<>nil check is to handle the case where the base64 string decodes to an empty array. If we were to remove that check then the following line would result in a runtime error if you were running with range checking enabled (which you should be doing). The call to stream.Write should be self-explanatory.

After looking into Soap.EncdDecd the one can find more platform independent way, as it's DecodeBase64 uses universal (no AnsiString) methods from System.NetEncoding.
Based on Uwe's sample:
uses
...
System.Classes,
System.NetEncoding;
...
procedure DecodeFile(const base64: String; const FileName: string);
var
stream: TBytesStream;
begin
stream := TBytesStream.Create(TNetEncoding.Base64.DecodeStringToBytes(base64));
try
stream.SaveToFile(Filename);
finally
stream.Free;
end;
end;

uses
Soap.EncdDecd;
function TForm1.EncodeFile(const FileName: string): AnsiString;
var
MemStream: TMemoryStream;
begin
MemStream := TMemoryStream.Create;
try
MemStream.LoadFromFile(Filename);
Result := EncodeBase64(MemStream.Memory, MemStream.Size);
finally
MemStream.Free;
end;
end;
function TForm1.DecodeFile(const base64: AnsiString): TBytesStream;
begin
Result := TBytesStream.Create(DecodeBase64(base64));
end;

I have a very old Delphi2006(v10.0.2558.35231 Update 2) and had to decode base64 UTF8 encoded input strings. I finally figured it out and heres an example for anyone interested.
Uses
IdCoderMIME; // Indy9
var
decoder: TIdDecoderMIME;
str: WideString;
- - -
decoder := TIdDecoderMIME.Create(nil);
str := base64DecodeUTF8(decoder, b64sourcestr);
decoder.Free;
- - -
function base64DecodeUTF8(decoder:TIdDecoderMIME; str:String): WideString;
var
stream:TMemoryStream;
utf8: UTF8String;
//idx:Integer;
begin
stream := TMemoryStream.Create;
try
decoder.DecodeToStream(str, stream);
setString(utf8, PChar(stream.Memory), stream.Size);
Result := UTF8Decode(utf8);
//for idx := 0 to stream.Size-1 do begin
// Writeln(PChar(stream.Memory)[idx] + ' ' + IntToStr(ORD(PChar(stream.Memory) [idx])) );
//end;
finally
stream.Free;
end;
end;

Related

In-memory compression of a Zip file

I want to compress files into Zip in-memory (I will store the result on a database instead of creating files in the file system).
How can I read the raw content for the compressed Zip file ?. I can't see any property to read that content, or any method to save that content into an Stream or any other in-memory structure.
function GetZip(UncompressedFile: TStream): TBytes;
var AZipFile: TZipFile;
begin
AZipFile := TZipFile.Create;
try
AZipFile.Add(UncompressedFile);
Result := AZipFile.Data; // ?? How to get the compressed file without saving it to the file system ??
finally
AZipfile.Free;
end;
end;
Thank you.
Thanks to AmigoJack and Remy Lebeau for letting me know that I can use the Zip input Stream to also get the result.
This works fine :
function Zip_AddFile(FileToAdd: TStream; Title: string): TBytes; overload;
begin
Result := Zip_AddFile([], FileToAdd, Title);
end;
function Zip_AddFile(Zip: TBytes; FileToAdd: TStream; Title: string): TBytes; overload;
var AZipFile: TZipFile;
ZipStream: TBytesStream;
begin
ZipStream := TBytesStream.Create(Zip);
AZipFile := TZipFile.Create;
try
AZipFile.Open(ZipStream, zmReadWrite);
AZipFile.Add(FileToAdd, Title);
AZipFile.Close;
Result := ZipStream.Bytes;
finally
AZipfile.Free;
ZipStream.Free;
end;
end;

How can I get a regular Delphi string from a stream after retrieving an object from Amazon S3?

I am putting a JSON string into Amazon S3 using the TAmazonStorageService class UploadObject method. When I retrieve the object it is placed in a stream (I am using a TStringStream), which appears to be coded in UTF-16 LE. If I then attempt to load that JSON into a memo, a TStringList, or any other similar object I get just the first character, the open curly brace of the JSON. On the other hand, if I write it to a file I get the entire JSON (UTF-16 LE encoded). I am assuming that because UTF-16 LE encodes each character with two bytes, and the second byte is always 0, Delphi is assuming that the 0 is the end of file marker.
How can I get a regular Delphi string (WideString), or even an ANSIString from the TStringStream, or is there another stream that I should use that I can use to get a WideString or ANSIString.
Here is pseudo code that represents the upload:
procedure StorePayload( AmazonConnectionInfo: TAmazonConnectionInfo; JSONString: String;
PayloadMemTable: TFDAdaptedDataSet;
PayloadType: String; PayloadVersion: Integer);
var
AmazonStorageService: TAmazonStorageService;
ab: TBytes;
ResponseInfo: TCloudResponseInfo;
ss: TStringStream;
Guid: TGuid;
begin
Guid := TGuid.NewGuid;
AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
try
// Write payload to S3
ResponseInfo := TCloudResponseInfo.Create;
try
ss := TStringStream.Create( JSONString );
try
ab := StringToBytes( ss.DataString );
if AmazonStorageService.UploadObject( BucketName, Guid.ToString, ab, false, nil, nil, amzbaPrivate, ResponseInfo ) then
PayloadMemTable.AppendRecord( [Guid.ToString, PayloadType, PayloadVersion, now() ] );
finally
ss.Free;
end;
finally
ResponseInfo.Free;
end;
finally
AmazonStorageService.Free;
end;
end;
And here is pseudo code that represents the retrieval of the JSON:
function RetrievePayload( AmazonConnectionInfo: TAmazonConnectionInfo ): String;
var
AmazonStorageService: TAmazonStorageService;
ObjectName: string;
ResponseInfo: TCloudResponseInfo;
ss: TStringStream;
OptParams: TAmazonGetObjectOptionals;
begin
// I tried with and without the TAmazonGetObjectOptionals
OptParams := TAmazonGetObjectOptionals.Create;
OptParams.ResponseContentEncoding := 'ANSI';
OptParams.ResponseContentType := 'text/plain';
AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
try
ss := TStringStream.Create( );
try
ResponseInfo := TCloudResponseInfo.Create;
try
if not AmazonStorageService.GetObject( BucketName, PayloadID, OptParams,
ss, ResponseInfo, amzrNotSpecified ) then
raise Exception.Create('Error retrieving item ' + ObjectName);
Result := ss.DataString;
// The memo will contain only {
Form1.Memo1.Lines.Text := ss.DataString;
finally
ResponseInfo.Free;
end;
finally
ss.Free;
end;
finally
AmazonStorageService.Free;
end;
end;
In Delphi 2009 and later, String is a UTF-16 UnicodeString, however TStringStream operates on 8-bit ANSI by default (for backwards compatibility with pre-Unicode Delphi versions).
There is no need for StorePayload() to use TStringStream at all. You are storing a String into the stream just to read a String back out from it. So just use the original String as-is.
Using StringToBytes() is unnecessary, too. You can, and should, use TEncoding.UTF8 instead, as UTF-8 is the preferred encoding for JSON data, eg:
procedure StorePayload( AmazonConnectionInfo: TAmazonConnectionInfo; JSONString: String;
PayloadMemTable: TFDAdaptedDataSet;
PayloadType: String; PayloadVersion: Integer);
var
AmazonStorageService: TAmazonStorageService;
ab: TBytes;
ResponseInfo: TCloudResponseInfo;
Guid: TGuid;
begin
Guid := TGuid.NewGuid;
AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
try
// Write payload to S3
ResponseInfo := TCloudResponseInfo.Create;
try
ab := TEncoding.UTF8.GetBytes( JSONString );
if AmazonStorageService.UploadObject( BucketName, Guid.ToString, ab, false, nil, nil, amzbaPrivate, ResponseInfo ) then
PayloadMemTable.AppendRecord( [Guid.ToString, PayloadType, PayloadVersion, Now() ] );
finally
ResponseInfo.Free;
end;
finally
AmazonStorageService.Free;
end;
end;
Conversely, when RetrievePayload() calls GetObject() later, you can use TEncoding.UTF8 with TStringStream to decode the String, eg:
function RetrievePayload( AmazonConnectionInfo: TAmazonConnectionInfo ): String;
var
AmazonStorageService: TAmazonStorageService;
ResponseInfo: TCloudResponseInfo;
ss: TStringStream;
begin
AmazonStorageService := TAmazonStorageService.Create( AmazonConnectionInfo );
try
ss := TStringStream.Create( '', TEncoding.UTF8 );
try
ResponseInfo := TCloudResponseInfo.Create;
try
if not AmazonStorageService.GetObject( BucketName, PayloadID, ss, ResponseInfo, amzrNotSpecified ) then
raise Exception.Create('Error retrieving item ' + ObjectName);
Result := ss.DataString;
Form1.Memo1.Text := Result;
finally
ResponseInfo.Free;
end;
finally
ss.Free;
end;
finally
AmazonStorageService.Free;
end;
end;
If you need to retrieve any pre-existing bucket objects that have already been uploaded as UTF-16, RetrievePayload() could use TEncoding.Unicode instead:
ss := TStringStream.Create( '', TEncoding.Unicode );
However, that won't work for newer objects uploaded with UTF-8. So, a more flexible solution would be to retrieve the raw bytes using a TMemoryStream or TBytesStream, then analyze the bytes to determine whether UTF8 or UTF-16 were used, and then use TEncoding.UTF8.GetString() or TEncoding.Unicode.GetString() to decode the bytes to a String.

Delphi Change Hex Address on file

I wanna over delphi change hex adress 15 character,
I follow like this a way but I didnt get success,
BlockRead(F,arrChar,1); //read all to the buf
CloseFile(F); //close file
IMEI:=Form1.Edit1.Text; //get the number
Form1.Memo1.Lines.Add('new IMEI is'+IMEI); //output
for i:=524288 to 524288+15 do /
arrChar[i]:=IMEI[i-524287];
Do this with a file stream.
var
Stream: TFileStream;
....
Stream := TFileStream.Create(FileName, fmOpenWrite);
try
Stream.Position := $080000;
Stream.WriteBuffer(IMEI, SizeOf(IMEI));
finally
Stream.Free;
end;
I'm assuming that IMEI is an fixed length array of bytes of length 15 but your code attempts to write 16 bytes so it would appear that you are suffering from a degree of confusion.
In your code, your variable IMEI is a string. Which is not an array of bytes. Please don't make that classic mistake of regarding a string as an array of bytes.
You might declare an IMEI type like this:
type
TIMEI = array [0..14] of Byte;
Then you might write a function to populate such a variable from text:
function TextToIMEI(const Text: string): TIMEI;
var
ResultIndex, TextIndex: Integer;
C: Char;
begin
if Length(Text) <> Length(Result) then
raise SomeExceptionClass.Create(...);
TextIndex := low(Text);
for ResultIndex := low(Result) to high(Result) do
begin
C := Result[TextIndex];
if (C < '0') or (C > '9') then
raise SomeExceptionClass.Create(...);
Result[ResultIndex] := ord(C);
inc(TextIndex);
end;
end;
You might then combine this code with that above:
procedure WriteIMEItoFile(const FileName: string; FileOffset: Int64; const IMEI: TIMEI);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenWrite);
try
Stream.Position := FileOffset;
Stream.WriteBuffer(IMEI, SizeOf(IMEI));
finally
Stream.Free;
end;
end;
Call it like this:
WriteIMEItoFile(FileName, $080000, TextToIMEI(Form1.Edit1.Text));
Although it looks a bit odd that you are explicitly using the Form1 global variable. If that code executes in a method of TForm1 then you should use the implicit Self variable.

Why doesn't TStringStream remove the BOM when converting to a string?

We have a library function that goes like this:
class function TFileUtils.ReadTextStream(const AStream: TStream): string;
var
StringStream: TStringStream;
begin
StringStream := TStringStream.Create('', TEncoding.Unicode);
try
// This is WRONG since CopyFrom might rewind the stream (see Remys comment)
StringStream.CopyFrom(AStream, AStream.Size - AStream.Position);
Result := StringStream.DataString;
finally
StringStream.Free;
end;
end;
When I check the string that is returned by the function the first Char is the (little-endian) BOM.
Why doesn't TStringStream ignore the BOM?
Is there a better way to do this? I don't need backwards compatibility with older Delphi versions, a working solution for XE2 would be fine.
The BOM has to be coming from the source TStream, as TStringStream does not write a BOM. If you want to ignore the BOM if it is present in the source, you have to do it manually before then copying the data, eg:
class function TFileUtils.ReadTextStream(const AStream: TStream): string;
var
StreamPos, StreamSize: Int64;
Buf: TBytes;
NumBytes: Integer;
Encoding: TEncoding;
begin
Result := '';
StreamPos := AStream.Position;
StreamSize := AStream.Size - StreamPos;
// Anything available to read?
if StreamSize < 1 then Exit;
// Read the first few bytes from the stream...
SetLength(Buf, 4);
NumBytes := AStream.Read(Buf[0], Length(Buf));
if NumBytes < 1 then Exit;
Inc(StreamPos, NumBytes);
Dec(StreamSize, NumBytes);
// Detect the BOM. If you know for a fact what the TStream data is encoded as,
// you can assign the Encoding variable to the appropriate TEncoding object and
// GetBufferEncoding() will check for that encoding's BOM only...
SetLength(Buf, NumBytes);
Encoding := nil;
Dec(NumBytes, TEncoding.GetBufferEncoding(Buf, Encoding));
// If any non-BOM bytes were read than rewind the stream back to that position...
if NumBytes > 0 then
begin
AStream.Seek(-NumBytes, soCurrent);
Dec(StreamPos, NumBytes);
Inc(StreamSize, NumBytes);
end else
begin
// Anything left to read after the BOM?
if StreamSize < 1 then Exit;
end;
// Now read and decode whatever is left in the stream...
StringStream := TStringStream.Create('', Encoding);
try
StringStream.CopyFrom(AStream, StreamSize);
Result := StringStream.DataString;
finally
StringStream.Free;
end;
end;
Apparently TStreamReader doesn't suffer from the same problem:
var
StreamReader: TStreamReader;
begin
StreamReader := TStreamReader.Create(AStream);
try
Result := StreamReader.ReadToEnd;
finally
StreamReader.Free;
end;
end;
TStringList also works (thanks whosrdaddy):
var
Strings: TStringList;
begin
Strings := TStringList.Create;
try
Strings.LoadFromStream(AStream);
Result := Strings.Text;
finally
Strings.Free;
end;
end;
I also measured both methods and TStreamReader seems to be about twice as fast.

Download crypted (XOR) content

I have one online TXT file encrypted with XOR. And I'm using Indy HTTP to read this file. When I do this:
Buff.Text:= HTTP.Get('http://www.blabla.com/xor.txt');
the content in Buff is corrupted and I can't decrypt it correctly. How to solve this? Below I'll paste the function I'm using to XOR the txt file:
function TForm1.XorStr(Input: AnsiString; Seed: integer): AnsiString;
var
i : integer;
Output : AnsiString;
begin
Output := '';
for i := 1 to Length(Input) do
Output := Output + AnsiChar(Ord(Input[i]) XOR (Seed));
Result:= Output;
end;
Hope someone can helps me out. Thank you guys!
You are downloading the data using the overloaded version of TIdHTTP.Get() that returns a UnicodeString. That version will decode the raw data to Unicode, based on the charset that is specified (or missing) in the server's Content-Type response header. For what you are attempting to do, that corrupts your data. You need to use the other overloaded version of TIdHTTP.Get() that fills a TStream with the raw data instead, then you can decode it, eg:
var
Strm: TMemoryStream;
Output: AnsiString;
begin
...
Strm := TMemoryStream.Create;
try
HTTP.Get('http://www.blabla.com/xor.txt', Strm);
Output := XorStr(Strm.Memory, Strm.Size, Seed);
finally
Strm.Free;
end;
...
end;
function TForm1.XorStr(Input: Pointer; InputSize: NativeInt; Seed: Integer): AnsiString;
var
i : integer;
begin
SetString(Result, PAnsiChar(Input), InputSize);
for i := 1 to Length(Result) do
Result[i] := AnsiChar(Ord(Result[i]) XOR Seed);
end;

Resources