Reputation: 13
Work on a project and I want to save the images I downloadin a blob field. I use idHTTP with success but UI freezes.
I create a procedure to download images from URL using ImageLoader method (Image Loader page). Everything is OK with the images but I cannot save them in the specific field in DB.
How can I do it?
Here is my code :
procedure TForm1.LoadImages;
var
i: integer;
IMLO: TImageLoader;
IM: TImage;
Stream: TMemoryStream;
imgAddress: string;
begin
Table1.First;
for i := 0 to Table1.RecordCount-1 do
begin
imgAddress := Table1.FieldByName('URL').AsString;
Stream := TMemoryStream.Create;
IM:= TImage.Create(self);
IM.Name := 'IM'+inttostr(i);
IM.Parent := HorzScrollBox1;
IM.Position.X := i * 320;
IM.Align:= TAlignLayout.alLeft;
IM.Width := 300;
IMLO.LoadImage(IM, imgAddress);
{ Table1.Edit;
TBlobField(Table1.FieldByName('image')).LoadFromStream(Stream);
Table1.Post;
Stream.Free; }
Table1.Next;
end;
end;
The code works perfectly if I want just to show images. How can I use stream to save them?
Upvotes: 1
Views: 173
Reputation: 76597
Problem
The problem is that the TImageLoader.LoadImage
loads the data from the net asynchronously.
Because of that the data is not yet available at the time when you're loading the data.
Simple solution
In order to stop TImageLoader from doing that you can add an extra method to it and call that one instead.
The other problem is that you are loading the Blob with the Stream
data, but you never load the stream with anything.
Luckily you don't need the stream.
function TImageLoader.LoadImageNow(const AImage: TImage; const AImageURL: string): boolean;
const
success = true;
failure = false;
var
MS : TMemoryStream;
Image: TImage;
begin
Result:= success;
MS := TMemoryStream.Create;
Image:= TImage.Create;
try
IdHTTP1.get(AImageURL,MS);
Ms.Seek(0,soFromBeginning);
Image.LoadFromStream(MS);
AImage.Picture.Assign(Image);
except
Result:= failure;
end;
FreeAndNil(Image);
FreeAndNil(MS);
end;
Now the loader will only return when the image is loaded.
This also explains why you get an exception upon freeing IM
.
You're freeing IM, but the loader is still using it in the background.
That's the problem with using multi-threaded applications, you need to keep track of what the other threads are doing.
The code for your loop will now look like this:
procedure TForm1.LoadImages;
var
i: integer;
IMLO: TImageLoader;
IM: TImage;
imgAddress: string;
FieldImage: TGraphicField;
FieldUrl: TField;
begin
FieldImage:= TGraphicField(Table1.FieldByName('image'));
FieldUrl:= Table1.FieldByName('URL');
Table1.First;
for i := 0 to Table1.RecordCount-1 do try
imgAddress := FieldUrl.AsString;
IM:= TImage.Create(self);
IM.Name := 'IM'+inttostr(i);
IM.Parent := HorzScrollBox1;
IM.Position.X := i * 320;
IM.Align:= TAlignLayout.alLeft;
IM.Width := 300;
if IMLO.LoadImageNow(IM, imgAddress) then begin
Table1.Edit;
FieldImage.Assign(IM.Picture);
Table1.Post;
end; {if}
finally
IM.Free;
Stream.Free;
Table1.Next;
end; {for .. try}
end;
Because FieldByName
is slow it is best to pull it out of a loop (it probably will not matter because of the slowness of LoadImageNow
).
See: http://docwiki.embarcadero.com/Libraries/XE5/en/Data.DB.TBlobField.Assign
And: http://docwiki.embarcadero.com/CodeExamples/XE3/en/HTTP_Get_%28Delphi%29
Other option
Add an event to the thread that you can hook into every time a image finishes loading.
In the event handler you can then display the image and put it into the database.
In fact the async loader may already have an option for this, but I do not know that codebase well enough to answer that question.
Upvotes: 1