I try to send a string from a tidtcpserver to a tidtcpclient in Delphi, but when i send a string the client receives nothing. I don't get any errors. I am using tstringstream because i want to send a base64 string(writeln/readln can't send/receive that much text)
This is the send code in idtcpserver1:
procedure TForm1.sendtext(index:integer; txt:string);
var
StrStream:tStream;
List: TList;
AContext: TIdContext;
begin
txt := trim(txt);
strstream := TMemoryStream.Create;
strstream.Write(PAnsiChar(txt)^, Length(txt));
strstream.Position := 0;
List := idTcpServer1.Contexts.LockList;
AContext := TIdContext(List[index]);
AContext.Connection.IOHandler.write(StrStream);
idTcpServer1.Contexts.UnLockList;
end;
This is the read code in idtcpclient1
procedure TIndyClient.Execute;
var
StrStream:tStringStream;
receivedtext:string;
begin
StrStream := tstringstream.Create;
form1.IdTCPClient1.IOHandler.readstream(StrStream);
receivedtext := strstream.DataString;
if receivedtext = '' = false then
begin
showmessage(receivedtext);
end;
end;
Does anyone know what i am doing wrong?
You are making a very common newbie mistake of mismatching the IOHandler.Write(TStream) and IOHandler.ReadStream() calls. By default, ReadStream() expects the stream data to be preceded by the stream size, but Write(TStream) does not send the stream size by default.
Try this instead:
procedure TForm1.sendtext(index: integer; const txt: string);
var
StrStream: TMemoryStream;
List: TList;
AContext: TIdContext;
begin
strStream := TMemoryStream.Create;
try
WriteStringToStream(StrStream, Trim(txt), enUTF8);
List := idTcpServer1.Contexts.LockList;
try
AContext := TIdContext(List[index]);
AContext.Connection.IOHandler.Write(StrStream, 0, True);
finally
idTcpServer1.Contexts.UnlockList;
end;
finally
StrStream.Free;
end;
end;
procedure TIndyClient.Execute;
var
StrStream: TMemoryStream;
receivedtext: string;
begin
StrStream := TMemoryStream.Create;
try
Form1.IdTCPClient1.IOHandler.ReadStream(StrStream, -1, False);
StrStream.Position := 0;
receivedtext := ReadStringFromStream(StrStream, enUTF8);
finally
StrStream.Free;
end;
if receivedtext <> '' then
begin
// ShowMessage() is not thread-safe...
MessageBox(0, PChar(receivedtext), PChar(Application.Title), MB_OK);
end;
end;
Alternatively, because the stream size is now being sent, the receiving code can use ReadString() instead of ReadStream():
procedure TIndyClient.Execute;
var
receivedtext: string;
begin
with Form1.IdTCPClient1.IOHandler do
begin
// you can alternatively set the IOHandler.DefStringEncoding
// instead of passing the encoding as a parameter...
receivedtext := ReadString(ReadLongInt, enUTF8);
end;
if receivedtext <> '' then
begin
// ShowMessage() is not thread-safe...
MessageBox(0, PChar(receivedtext), PChar(Application.Title), MB_OK);
end;
end;
BTW, WriteLn()/ReadLn() do not have any length restrictions, they can handle big strings as long as there is available memory (and the text being sent does not have any line breaks in it). You are probably being confused by ReadLn() being subject to the IOHandler.MaxLineLength, which is set to 16K characters by default, but you can bypass that:
procedure TForm1.sendtext(index: integer; const txt: string);
va
List: TList;
AContext: TIdContext;
begin
List := idTcpServer1.Contexts.LockList;
try
AContext := TIdContext(List[index]);
// you can alternatively set the IOHandler.DefStringEncoding
// instead of passing the encoding as a parameter...
AContext.Connection.IOHandler.WriteLn(Trim(txt), enUTF8);
finally
idTcpServer1.Contexts.UnlockList;
end;
end;
procedure TIndyClient.Execute;
var
receivedtext: string;
begin
// you can alternatively set the IOHandler.DefStringEncoding
// and IOHandler.MaxLineLength instead of passing them as parameters...
receivedtext := Form1.IdTCPClient1.IOHandler.ReadLn(LF, -1, MaxInt, enUTF8);
if receivedtext <> '' then
begin
// ShowMessage() is not thread-safe...
MessageBox(0, PChar(receivedtext), PChar(Application.Title), MB_OK);
end;
end;
Related
I want to transfer data from the TIdTCPServer to the TIdTCPClient.
On the server side I have:
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var x:Integer;
Received:String;
SendBuff:TBytes;
hFile:THandle;
fSize:Int64;
begin
fSize:=0;
if MOpenFileForRead(hFile,MGetExePath+'\test.jpg') then begin
fSize:=MFileSize(hFile);
SetLength(SendBuff,fSize);
MReadFile(hFile,SendBuff[0],fSize);
MCloseFile(hFile);
end;
// ... here the SendBuff contains valid data, I checked.
repeat
Received:=AContext.Connection.Socket.ReadLn;
if not AContext.Connection.Connected then Exit;
if Received=CMD_TEST_FILE then begin
AContext.Connection.Socket.Write(fSize);
AContext.Connection.Socket.WriteBufferOpen;
AContext.Connection.Socket.Write(SendBuff);
AContext.Connection.Socket.WriteBufferClose;
end;
until False;
end;
And the client side:
procedure TForm1.Button2Click(Sender: TObject);
var fSize:Int64;
RecvBuff:TBytes;
hFile:THandle;
begin
IdTCPClient1.Socket.WriteLn(CMD_TEST_FILE);
fSize:=IdTCPClient1.Socket.ReadInt64;
SetLength(RecvBuff,fSize);
IdTCPClient1.Socket.ReadBytes(RecvBuff,fSize);
if MCreateFile(hFile, MGetExePath+'\new.jpg') then begin
MWriteFile(hFile,RecvBuff[0],fSize);
MCloseFile(hFile);
end;
Memo1.Lines.Add('ok');
end;
... but it's not working. I checked the read and write data functions used and they are ok. At the server the buffer is set ok, the file size arrives at client ok, but the content of the buffer at client is only zeros.
P.S: I want to send the file in this way not with stream or anything else.
If you look at the signature of ReadBytes(), it has an optional AAppend parameter that is True by default:
procedure ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True); virtual;
When true, it reads bytes from the socket and appends them to the end of the existing byte array. Since you are pre-allocating the array, the initial bytes are undefined and the file bytes follow after the undefined bytes.
To fix this, you need to either:
Stop pre-allocating the byte array, let ReadBytes() allocate it for you.
procedure TForm1.Button2Click(Sender: TObject);
var
fSize: Int64;
RecvBuff: TBytes;
hFile: THandle;
begin
IdTCPClient1.Socket.WriteLn(CMD_TEST_FILE);
fSize := IdTCPClient1.Socket.ReadInt64;
// SetLength(RecvBuff,fSize); // <-- remove this line
IdTCPClient1.Socket.ReadBytes(RecvBuffer, fSize);
if MCreateFile(hFile, MGetExePath+'\new.jpg') then
begin
MWriteFile(haile, RecvBuff[0], fSize);
MCloseFile(hFile);
end;
Memo1.Lines.Add('ok');
end;
pre-allocate the array, but set AAppend to False so the bytes fill the existing array instead of append to it.
procedure TForm1.Button2Click(Sender: TObject);
var
fSize: Int64;
RecvBuff: TBytes;
hFile: THandle;
begin
IdTCPClient1.Socket.WriteLn(CMD_TEST_FILE);
fSize := IdTCPClient1.Socket.ReadInt64;
SetLength(RecvBuff, fSize);
IdTCPClient1.Socket.ReadBytes(RecvBuff, fSize, False);
if MCreateFile(hFile, MGetExePath+'\new.jpg') then
begin
MWriteFile(haile, RecvBuff[0], fSize);
MCloseFile(hFile);
end;
Memo1.Lines.Add('ok');
end;
Update: That being said, I strongly suggest you use a TStream instead, despite you saying you do not want to. It will greatly simplify the code and memory management, without breaking the communication protocol you have chosen to use:
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
AContext.Data := TFileStream.Create(MGetExePath+'\test.jpg', fmOpenRead or fmShareDenyWrite);
AContext.Connection.IOHandler.LargeStream := True;
end;
TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
Received: String;
begin
Received := AContext.Connection.IOHandler.ReadLn;
if Received = CMD_TEST_FILE then
begin
AContext.Connection.IOHandler.Write(TStream(AContext.Data), 0, True);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
FileName: string;
Strm: TStream;
begin
FileName := MGetExePath+'\new.jpg';
Strm := TFileStream.Create(FileName, fmCreate);
try
try
IdTCPClient1.IOHandler.WriteLn
(CMD_TEST_FILE);
IdTCPClient1.IOHandler.ReadStream(Strm, -1, False);
finally
Strm.Free;
end;
except
DeleteFile(FileName);
raise;
end;
Memo1.Lines.Add('ok');
end;
I am trying to send a TStringStream from client to server, then send it back from server to client, using Indy TCP components.
Here is my client code:
var
Jpg: TJPEGImage;
StringStream: TStringStream;
strcams, StringImageData: String;
byt, i: integer;
procedure SendCommandWithParams(Command, Params: String);
begin
Lock;
try
if not FTCP.Connected then
begin
exit;
end;
FTCP.Socket.WriteLn('1' + Command, IndyTextEncoding_UTF8);
FTCP.Socket.WriteLn(Params, IndyTextEncoding_UTF8);
finally
Unlock;
end;
end;
begin
Jpg := TJPEGImage.Create;
StringStream := TStringStream.Create('');
try
try
Jpg.Performance := jpBestSpeed;
Jpg.ProgressiveEncoding := True;
Jpg.ProgressiveDisplay := True;
Jpg.Assign(Image2.Picture.Bitmap);
Jpg.CompressionQuality := 25;
Jpg.Compress;
Jpg.SaveToStream(StringStream);
StringImageData := StringStream.DataString;
strcams := '<[S:' + IntToStr(Length(StringImageData)) + 'B]>' +
StringImageData;
if Length(strcams) < byt then
begin
SendCommandWithParams('SIMGSEND', strcams + sep + 'IMGID5423' + sep);
end;
except
on e: exception do
//
end;
finally
StringImageData := '';
FreeAndNil(Jpg);
FreeAndNil(StringStream);
end;
end;
I can receive the TStringStream data, but the data received is corrupted, and some times it gets replaced with the second parameter that I send which is 'IMGID5423' + sep. I am not sure if this is because of some limit of packet sending through TCP so the data does not arrive complete, or is this a parser issue?
My current parser should separate each text that ended with #13#10. Here is how it looks:
var
ReceiveParams, ReceiveStream: Boolean;
S: string;
Command: String;
begin
Command := Fholdcommand;
ReceiveParams := false;
ReceiveStream := false;
if Command[1] = '1' then // command with params
begin
Command := Copy(Command, 2, MaxInt);
ReceiveParams := True;
end;
if ReceiveParams then // params incomming
begin
S := FTCP.Socket.ReadLn(IndyTextEncoding_UTF8);
FCMD := Command;
FPRMS := S;
FSTREAM := false;
if Assigned(FOnCallbackProc) then
begin
Synchronize(DoCallbackProc);
end;
end;
I am still confused about the real issue. I try to send the TStringStream in a local procedure, and it is received normally without any corruption.
Am I sending the data wrong altogether through Indy?
This is how I am receiving the data:
procedure CreateJpg(Data:string);
var
StringStream : TStringStream;
JpegImage : TJPEGImage;
Bitmap : TBitmap;
tmpPos:integer;
pp:string;
label check;
begin
GData := Data;
if LeftStr(GData,4) = '<[S:' then
begin
tmpPos := Pos(WideString('B]>'),GData);
pp := Copy(GData,5,tmpPos-5);
CDataELen := StrToInt(pp); //MidStr(st,5,tmppos - 5);
CData := RightStr(GData,length(GData)-(tmppos+2));
goto check;
end;
CData := CData + GData;
check:
//if CDataELen = length(CData) then
begin
StringStream := TStringStream.Create('');
JpegImage := TJpegImage.Create;
StringStream.WriteString(CData);
CData := '';
try
try
StringStream.Seek(0, soFromBeginning);
JpegImage.LoadFromStream(StringStream);
Bitmap := TBitmap.Create;
with Bitmap do
begin
Canvas.Lock;
try
Width := JpegImage.Width;
Height := JpegImage.Height;
Canvas.Draw(0, 0, JpegImage);
finally
Canvas.Unlock;
end;
end;
img.Picture.Bitmap.Width := Bitmap.Width;
img.Picture.Bitmap.Height := Bitmap.Height;
img.Picture.Bitmap.Canvas.Draw(0, 0, Bitmap);
except
on E: Exception do
//
end;
finally
FreeAndNil(StringStream);
FreeAndNil(JpegImage);
FreeAndNil(Bitmap);
end;
end;
end;
The problem is that you are saving the JPG binary data to a TStringStream and then letting it reinterpret the binary data as if it were string data. You can't do that. You need to save the JPG data to a binary stream instead, like TMemoryStream, and then encode the binary data using a string-safe encoding, like Base64.
Try something more like this instead:
uses
..., IdCoder, IdCoderMIME;
...
var
Jpg: TJPEGImage;
JpegStream: TMemoryStream;
strcams, StringImageData: String;
begin
try
JpegStream := TMemoryStream.Create;
try
Jpg := TJPEGImage.Create;
try
Jpg.Performance := jpBestSpeed;
Jpg.ProgressiveEncoding := True;
Jpg.ProgressiveDisplay := True;
Jpg.Assign(Image2.Picture.Bitmap);
Jpg.CompressionQuality := 25;
Jpg.Compress;
Jpg.SaveToStream(JpegStream);
finally
Jpg.Free;
end;
JpegStream.Position := 0;
StringImageData := TIdEncoderMIME.EncodeStream(JpegStream);
finally
JpegStream.Free;
end;
strcams := '<[S:' + IntToStr(Length(StringImageData)) + 'B]>' + StringImageData;
SendCommandWithParams('SIMGSEND', strcams + sep + 'IMGID5423' + sep);
except
on e: exception do
//
end;
end;
And then on the receiving end:
procedure CreateJpg(Data: string);
var
JpegStream: TMemoryStream;
JpegImage: TJPEGImage;
Bitmap: TBitmap;
tmpPos, tmpLen: integer;
pp: string;
begin
try
if not TextStartsWith(Data, '<[S:') then
begin
// bad data, do something else...
Exit;
end;
tmpPos := Pos('B]>', Data);
pp := Copy(Data, 5, tmpPos-5);
tmpLen := StrToInt(pp);
Data := Copy(Data, tmpPos+3, tmpLen);
Bitmap := TBitmap.Create;
try
JpegImage := TJpegImage.Create;
try
JpegStream := TMemoryStream.Create;
try
TIdDecoderMIME.DecodeStream(Data, JpegStream);
JpegStream.Position := 0;
JpegImage.LoadFromStream(JpegStream);
finally
JpegStream.Free;
end;
with Bitmap do
begin
Canvas.Lock;
try
Width := JpegImage.Width;
Height := JpegImage.Height;
Canvas.Draw(0, 0, JpegImage);
finally
Canvas.Unlock;
end;
end;
finally
JpegImage.Free;
end;
img.Picture.Assign(Bitmap);
finally
Bitmap.Free;
end;
except
on E: Exception do
//
end;
end;
Your problem appears to be that you are treating binary data as though it is text. Binary data can contain anything, for instance #13#10 line breaks or indeed anything whatsoever.
If you wish to send that data as text, then you need to use a text encoding. For example, encode it as base64.
Or transmit the content as binary rather than text.
I have a simple mobile application written in Delphi XE8 that allows the user to take a picture and then send the picture to a server using Indy TCPClient/TCP Server.
I have scoured the forums and found numerous examples to send the data in a variety of ways. Every method I try results in an access violation or corrupt data on the server side.
My ultimate goal is to send a record containing a unique identifier, description and a picture(bitmap) from the client to the server.
But I'm starting out be trying to simply send a record with some text from a windows client to the server. I will then try to implement the solution into my mobile app.
type
TSendRec = record
// SONo: string;
Text: string;
// Bitmap: TBitMap;
end
I have tried the following 3 methods as per the code below:
Send Using a Stream
Send using RawToBytes and TIDBytes.
Send a line of text using Writeln and Readln
When I try to send using a stream I get the following access violation:
Project memorystream_server.exe raised the exception class $C0000005 with message 'access violation at 0x00409e46: write of address 0x0065d1bc
The error occurs when I try to access the value of MiRec.Text on the server side.
Memo1.Lines.Add(MiRec.Text);
So I assume the read of the MIRec is failing for some reason:
When I send using RawToBytes, no error message occurs but the value of MIRec.Text is garbage.
When I just send a line of text using WriteLn, the server receives and displays the data correctly and no access violation occurs.
I tried to follow examples that I have found from other posts on this issue. I would greatly appreciate any insight into what I am doing wrong.
Following are my client and server side code snippets:
Client
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
Buffer: TIdBytes;
MIRec: TSendRec;
msRecInfo: TMemoryStream;
msRecInfo2: TIdMemoryBufferStream;
begin
IdTCPClient1.Connect;
MIRec.Text := 'Hello World';
if rbSendStream.Checked then
begin
msRecInfo := TMemoryStream.Create;
try
msRecInfo.Write(MIRec, SizeOf(MIRec));
IdTCPClient1.IOHandler.Write(msRecInfo, 0, False);
finally
msRecInfo.Free;
end;
{
msRecInfo2 := TIdMemoryBufferStream.Create(#MIRec, SizeOf(TSendRec));
try
IdTCPClient1.IOHandler.Write(msRecInfo2);
finally
msRecInfo.Free;
end;
}
end
else
if rbSendBytes.Checked then
begin
Buffer := RawToBytes(MIRec, SizeOf(MIRec));
IdTCPClient1.IOHandler.Write(Buffer);
end
else
if rbWriteLn.Checked then
begin
IdTCPClient1.Socket.WriteLn(Edit1.Text);
end;
IdTCPClient1.DisConnect;
end;
Server
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var sName: String;
MIRec: TSendRec;
Buffer: TIdBytes;
msRecInfo: TMemoryStream;
begin
if not chkReceiveText.Checked then
begin
try
if chkReadBytes.Checked then
begin
AContext.Connection.IOHandler.ReadBytes(Buffer, SizeOf(MIRec));
BytesToRaw(Buffer, MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
end
else
begin
msRecInfo := TMemoryStream.Create;
try
// does not read the stream size, just the stream data
AContext.Connection.IOHandler.ReadStream(msRecInfo, SizeOf(MIRec), False);
msRecInfo.Position := 0;
msRecInfo.Read(MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
finally
msRecInfo.Free;
end;
{
AContext.Connection.IOHandler.ReadStream(msRecInfo, -1, False);
msRecInfo.Position := 0;
msRecInfo.Read(MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
}
end;
Memo1.Lines.Add('read File');
except
Memo1.Lines.Add('error in read File');
end;
end
else
begin
sName := AContext.Connection.Socket.ReadLn;
Memo1.Lines.Add(sName);
end;
AContext.Connection.Disconnect;
end;
TIdTCPServer is a multithreaded component. Its OnConnect, OnDisconnect, and OnExecute events are triggered in the context of a worker thread. As such, you MUST synchronize with the main UI thread when accessing UI controls, like your Memo.
Also, String is a compiler-managed data type, and TBitmap is an object. Both store their data elsewhere in memory, so you cannot write a record containing such fields as-is. You would be writing only the value of their data pointers, not writing the actual data being pointed at. You need to serialize your record into a transmittable format on the sending side, and then deserialize it on the receiving side. That means handling the record fields individually.
Try something more like this:
type
TSendRec = record
SONo: string;
Text: string;
Bitmap: TBitMap;
end;
Client
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
MIRec: TSendRec;
ms: TMemoryStream;
begin
MIRec.SONo := ...;
MIRec.Text := 'Hello World';
MIRec.Bitmap := TBitmap.Create;
...
try
IdTCPClient1.Connect;
try
IdTCPClient1.IOHandler.WriteLn(MIRec.SONo);
IdTCPClient1.IOHandler.WriteLn(MIRec.Text);
ms := TMemoryStream.Create;
try
MIRec.Bitmap.SaveToStream(ms);
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.IOHandler.Write(ms, 0, True);
finally
ms.Free;
end;
finally
IdTCPClient1.Disconnect;
end;
finally
MIRec.Bitmap.Free;
end;
end;
Server
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
MIRec: TSendRec;
ms: TMemoryStream;
begin
MIRec.SONo := AContext.Connection.IOHandler.ReadLn;
MIRec.Text := AContext.Connection.IOHandler.ReadLn;
MIRec.Bitmap := TBitmap.Create;
try
ms := TMemoryStream.Create;
try
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.ReadStream(ms, -1, False);
ms.Position := 0;
MIRec.Bitmap.LoadFromStream(ms);
finally
ms.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(MIRec.SONo);
Memo1.Lines.Add(MIRec.Text);
// display MIRec.Bitmap as needed...
end;
end;
finally
MIRec.Bitmap.Free;
end;
end;
Alternatively:
Client
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
MIRec: TSendRec;
ms: TMemoryStream;
procedure SendString(const S: String);
var
Buf: TIdBytes;
begin
Buf := IndyTextEncoding_UTF8.GetBytes(S);
IdTCPClient1.IOHandler.Write(Int32(Length(Buf)));
IdTCPClient1.IOHandler.Write(Buf);
end;
begin
MIRec.SONo := ...;
MIRec.Text := 'Hello World';
MIRec.Bitmap := TBitmap.Create;
...
try
IdTCPClient1.Connect;
try
SendString(MIRec.SONo);
SendString(MIRec.Text);
ms := TMemoryStream.Create;
try
MIRec.Bitmap.SaveToStream(ms);
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.IOHandler.Write(ms, 0, True);
finally
ms.Free;
end;
finally
IdTCPClient1.Disconnect;
end;
finally
MIRec.Bitmap.Free;
end;
end;
Server
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
MIRec: TSendRec;
ms: TMemoryStream;
function RecvString: String;
begin
Result := AContext.Connection.IOHandler.ReadString(
AContext.Connection.IOHandler.ReadInt32,
IndyTextEncoding_UTF8);
end;
begin
MIRec.SONo := RecvString;
MIRec.Text := RecvString;
MIRec.Bitmap := TBitmap.Create;
try
ms := TMemoryStream.Create;
try
AContext.Connection.IOHandler.ReadStream(ms, -1, False);
ms.Position := 0;
MIRec.Bitmap.LoadFromStream(ms);
finally
ms.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(MIRec.SONo);
Memo1.Lines.Add(MIRec.Text);
// display MIRec.Bitmap as needed...
end;
end;
finally
MIRec.Bitmap.Free;
end;
end;
I try to insert into blob field in SQLite with Delphi XE3.
I've been using TDBXCommand and TSQLConnection like this.
but blob field is not inserted and even i cannnot get any result from query
procedure TDBXCommandHelper.Init(const AQry: String);
begin
Parameters.ClearParameters;
Close;
Prepare;
Text := AQry;
end;
procedure dmDB.InsertPicture;
const
QRY = 'INSERT INTO Memo(Picture) VALUES(?)';
var
LTransaction: TDBXTransaction;
LBlob: TDBXParameter;
LStream: TFileStream;
begin
LTransaction := FDBCon.BeginTransaction;
LStream := TFileStream.Create('d:\sample.bmp', fmOpenRead);
LBlob := TDBXParameter.Create;
try
try
FDBXCmd := FDBCon.DBXConnection.CreateCommand;
FDBXCmd.CommandType := TDBXCommandTypes.DbxSQL;
FDBXCmd.Init(QRY);
LBlob.DataType := TDBXDataTypes.BlobType;
LBlob.SubType := TDBXSubDataTypes.BinarySubType;
LBlob.Value.SetStream(LStream, False);
FDBXCmd.Parameters.AddParameter(LBlob);
FDBXCmd.ExecuteUpdate;
except
on E: Exception do
FDBCon.RollbackFreeAndNil(LTransaction);
end;
FDBCon.CommitFreeAndNil(LTransaction);
finally
FreeAndNil(LStream);
FreeAndNil(LBlob);
end;
end;
using TSQLConnection but i cannot get any result
procedure TInsertThread.NoteInsertExcute;
const
QRY = 'INSERT INTO Memo(Picture) VALUES(:Picture)';
var
LTransaction: TDBXTransaction;
LParams: TParams;
LStream: TMemoryStream;
begin
LTransaction := FDBCon.BeginTransaction;
LParams := TParams.Create(nil);
LStream := TMemoryStream.Create;
LStream.LoadFromFile(FValues.Values[NAME_PICTURE]);
try
LParams.CreateParam(ftBlob, 'Picture', ptInput);
LParams.ParamByName('Picture').LoadFromStream(LStream, ftBlob);
FDBCon.Execute(QRY, LParams);
FDBCon.CommitFreeAndNil(LTransaction);
finally
FreeAndNil(LStream);
FreeAndNil(LParams);
end;
end;
That's easy like:
var
ms: TMemoryStream;
sq: TSQLQuery;
begin
ms := TMemoryStream.Create;
ms.LoadFromFile('C:\Pictures\l.jpg');
if ms <> nil then
begin
sq := TSQLQuery.Create(nil);
sq.SQLConnection := con1;
sq.SQL.Text := 'update db1 set picture= :photo ;';
sq.Params.ParseSQL(sq.SQL.Text, true);
sq.Params.ParamByName('photo').LoadFromStream(ms, ftBlob);
sq.ExecSQL();
end;
.
Where con1 is a TSQLConnection.
You can try the following:
function GetFileAsBytesValue(AFileName: TFileName): TArray<Byte>;
var
Len: Integer;
LStream: TMemoryStream;
begin
LStream := TMemoryStream.Create;
try
LStream.LoadFromFile(AFileName);
Len := LStream.Size;
SetLength(Result, Len);
Move(LStream.Memory^, Result[0], Len);
finally
LStream.Free;
end;
end;
procedure dmDB.InsertPicture;
const
QRY = 'INSERT INTO Memo(Picture) VALUES(?)';
var
LTransaction: TDBXTransaction;
LDBXCmd: TSQLQuery;
LParam: TParam;
begin
LTransaction := FDBCon.BeginTransaction;
LDBXCmd := TSQLQuery.Create(FDBCon);
try
try
LDBXCmd.SQLConnection := FDBCon;
LDBXCmd.SQL.Text := QRY;
LParam := LDBXCmd.Params.CreateParam(ftBlob, 'Picture', ptInput);
LParam.AsBlob := GetFileAsBytesValue('d:\sample.bmp');
LDBXCmd.ExecSQL;
except
on E: Exception do
FDBCon.RollbackFreeAndNil(LTransaction);
end;
FDBCon.CommitFreeAndNil(LTransaction);
finally
LDBXCmd.Free;
end;
end;
I use the following code to eval the msg. content (body / lines) of an E Mail msg received with the INDY 10 components
function LinesFromMsg(aMsg: TIdMessage): TStrings;
var
i: Integer;
begin
for i := 0 to aMsg.MessageParts.AttachmentCount-1 do
begin
if (amsg.MessageParts.Items[i].ContentType ='HTML') then
begin
if (amsg.MessageParts.Items[i] is Tidtext) then
Result := TidText(amsg.MessageParts.Items[i]).body;
end;
end;
end;
regarding this code I have 2 questions :
a) is this the correct way of finding the Tlines part in an arbitray mail message ?
( consider the advice shown at INDY 10 EMAIL MSG PARTS )
b) where can I find a tutorial of all the different Contenttype string values?
The correct ContentType value to look for is text/html. Use Indy's IsHeaderMediaType() function to check it, as the ContentType value may have additional attributes associated with it that your comparison needs to ignore.
You also need to take the TIdMessage.ContentType into account as well, as HTML emails may not be MIME encoded and thus not use the TIdMessage.MessageParts` collection at all.
And lastly, you loop needs to use the MessageParts.Count property instead of the MessageParts.AttachmentsCount property.
Try this:
function HTMLFromMsg(aMsg: TIdMessage): TStrings;
var
i: Integer;
Part: TIdMessagePart;
begin
Result := nil;
if IsHeaderMediaType(aMsg.ContentType, 'text/html') then
begin
Result := aMsg.Body;
Exit;
end;
for i := 0 to aMsg.MessageParts.Count-1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part is TIdText) and IsHeaderMediaType(Part.ContentType, 'text/html') then
begin
Result := TIdText(Part).Body;
Exit;
end;
end;
end;
With that said, this is technically not the correct way to handle MIME. Officially, a conforming reader is supposed to loop backwards through the MIME parts, as they are ordered from the simpliest form downwards towards the most complex form. So you loop backwards, taking MIME nesting into account, looking for the most complex form you support. Something more like this (untested):
procedure DisplayPlainText(Body: TStrings);
begin
// display plain text as needed...
end;
procedure DisplayHTML(Body: TStrings);
begin
// display html as needed...
end;
procedure DisplayMultiPartAlternative(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
Part: TIdMessagePart;
i: Integer:
begin
for i := aLastIndex-1 downto aParentIndex+1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
begin
if IsHeaderMediaType(Part.ContentType, 'text/html') then
begin
DisplayHTML(TIdText(Part).Body);
Exit;
end;
if IsHeaderMediaType(Part.ContentType, 'text/plain') then
begin
DisplayPlainText(TIdText(Part).Body);
Exit;
end;
end;
end;
// nothing supported to display...
end;
procedure DisplayMultiPartMixed(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
Part: TIdMessagePart;
i: Integer;
begin
for i := aLastIndex-1 downto aParentIndex+1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
begin
if IsHeaderMediaType(Part.ContentType, 'multipart/alternative') then
begin
DisplayMultiPartAlternative(aMsg, ParentPart.Index, aLastIndex);
Exit;
end;
if IsHeaderMediaType(ParentPart.ContentType, 'text/html') then
begin
DisplayHTML(TIdText(Part).Body);
Exit;
end;
if IsHeaderMediaType(Part.ContentType, 'text/plain') then
begin
DisplayPlainText(TIdText(Part).Body);
Exit;
end;
aLastIndex := i;
end;
end;
// nothing supported to display...
end;
procedure DisplayMsg(aMsg: TIdMessage);
var
ContentType: string;
begin
ContentType := ExtractHeaderMediaType(aMsg.ContentType);
case PosInStrArray(ContentType, ['multipart/mixed', 'multipart/alternative', 'text/html', 'text/plain'], False) of
0: begin
DisplayMultiPartAlternative(aMsg, -1, aMsg.MessageParts.Count);
Exit;
end;
1: begin
DisplayMultiPartMixed(aMsg, -1, aMsg.MessageParts.Count);
Exit;
end;
2: begin
DisplayHTML(aMsg.Body);
Exit;
end;
3: begin
DisplayPlainText(aMsg.Body);
Exit;
end;
else
// nothing supported to display...
end;
end;