[Delphi] JPEG error #41 bij laden van image uit database

Pagina: 1
Acties:
  • 268 views sinds 30-01-2008
  • Reageer

Acties:
  • 0 Henk 'm!

Anoniem: 14829

Topicstarter
Ik kan me aardig redden in Delphi (niet aan m'n baas vertellen als je 't er niet mee eens bent ;)), maar ik struikel nu over een probleempje waar ik niet zo 1-2-3 uitkom:
Ik heb een component dat allerlei data uit een database kan plukken en daar dan een browse- en editform van kan genereren, en die wil ik nu uitbreiden zodat 'ie ook met JPEG plaatjes overweg kan. En daar begint mijn probleem...

Plaatjes vanuit het editform laden gaat prima (m.b.v. een TOpenPictureDialog), en hij slaat 'm ook netjes op in de database (MSSQL, veldtype is 'image') met de volgende code:
Delphi:
1
2
3
4
5
6
7
8
// save image
tmpStream := TMemoryStream.Create;
try
  TJPEGImage(CurrentImage.Picture.Graphic).SaveToStream(tmpStream);
  TBlobField(CurrentField).LoadFromStream(tmpStream);
finally
  tmpStream.Free;
end;

CurrentImage is een TImage, en CurrentField een TField, en de data wordt daarna natuurlijk nog opgeslagen. Lijkt allemaal goed te gaan, en ook het teruglezen levert zo te zien geen problemen op:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
// load image
tmpStream := TMemoryStream.Create;
try
  TBlobField(CurrentField).SaveToStream(tmpStream);
  tmpStream.Position := 0;
  jpg := TJPEGImage.Create;
  jpg.LoadFromStream(tmpStream);
  CurrentImage.Picture.Graphic := jpg;
finally
  tmpStream.Free;
end;

Hij leest netjes 175666 bytes uit de database, en jpg.Width en jpg.Height zijn daarna ook 768 * 1024 (de grootte van 't plaatje), maar als ik 'm daarna wil tonen, krijg ik een 'JPEG error #41'.
Als ik op die foutmelding ga googlen, krijg ik dingen als: bestand bestaat niet of heeft een lengte van 0 bytes. Er bestaat inderdaad geen bestand, want de data wordt rechtstreeks gestreamed vanuit de database...

Iemand een idee wat ik hier fout doe?

O ja, alle controls en datasets worden dynamisch gecreeerd, en die TImage staat op een custom control (een TPanel) om het resizen gemakkelijker te maken en om het popup menu (load/save/clear picture) wat toegankelijker te maken.

offtopic:
Spijtig dat Borland de source van de jpeg unit niet vrij kan / mag geven...

Acties:
  • 0 Henk 'm!

  • FendtVario
  • Registratie: Januari 2002
  • Laatst online: 12-05 22:30

FendtVario

The leader drives Vario!

Ik kan zo niet direct een fout zien in je code. Ik heb het even geprobeert met een bestand omdat ik niet direct een database heb (en zeker geen MSSQL). Wat gebeurt er als je de BLOB op slaat als bestand en dat probeert te lezen?

Wat je ook zou kunnen proberen is
Delphi:
1
tmpStream.Seek(0, soFromBeginning);
ipv
Delphi:
1
tmpStream.Position := 0

www.fendt.com | Nikon D7100 | PS5


Acties:
  • 0 Henk 'm!

Anoniem: 14829

Topicstarter
't Enige dat TStream.Position doet is:
Delphi:
1
2
3
4
procedure TStream.SetPosition(Pos: Longint);
begin
  Seek(Pos, 0);
end;

Wegschrijven naar een temp file en dan in de TImage laden werkt prima, en zo doe ik 't nu ook, maar ik vind 't eigenlijk geen nette oplossing...

Acties:
  • 0 Henk 'm!

  • CyeZ
  • Registratie: September 2001
  • Laatst online: 23-05 06:23

CyeZ

Vroem vroem!!!

Je kunt eens proberen of inplaats van

code:
1
CurrentImage.Picture.Graphic := jpg;


het volgende wel werkt.

code:
1
CurrentImage.Picture.Assign(jpg);

[18:54] <Prammenhanger> |HunterPro|eet
[18:55] <Prammenhanger> lijkt best op
[18:55] <Prammenhanger> |HunterProFeet


Acties:
  • 0 Henk 'm!

  • FendtVario
  • Registratie: Januari 2002
  • Laatst online: 12-05 22:30

FendtVario

The leader drives Vario!

Naar een tempfile wegschrijven klinkt inderdaad niet zo netjes maar is misschien voorlopig wel een oplossing. Zijn er standaard functies in windows om een random tempfile te maken? Van unix weet ik dat hiervoor tempfile bestaat die een random bestandsnaam teruggeeft.

Wat gebeurt er als je de stream uit de blob kopieert naar een andere TMemoryStreaam. In een testje wat ik gister deed kopieerde ik de FileStream naar een MemoryStream wat ook werkte. Zal wel niet maar houdt je weer ff van de straat ;)

www.fendt.com | Nikon D7100 | PS5


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
FendtVario schreef op dinsdag 14 juni 2005 @ 17:43:
Naar een tempfile wegschrijven klinkt inderdaad niet zo netjes maar is misschien voorlopig wel een oplossing. Zijn er standaard functies in windows om een random tempfile te maken? Van unix weet ik dat hiervoor tempfile bestaat die een random bestandsnaam teruggeeft.
http://msdn.microsoft.com...io/fs/gettempfilename.asp :Y)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Anoniem: 14829

Topicstarter
CyeZ, zowel de TPicture.Graphic = ... manier als TPicture.Assign() komen op hetzelfde uit: de TPicture.SetGraphic method. Dat maakt dus niet zo gek veel uit.

FendtVario, er is een Windows call om een temp filenaam aan te maken (GetTempFileName()), maar dat was me te omslachtig. Ik pak gewoon de machinenaam + FormatDateTime('yymmddhhnnsszzz', Now). Da's uniek genoeg. :)

Acties:
  • 0 Henk 'm!

Anoniem: 146949

Ik denk echt dat het aan je SQL DB ligt, of in ieder geval aan de velddefinitie(s).

Ik heb het uitgeprobeerd met een kleine PARADOX tabel. met daarin 3 soorten Blobs.

Type B (Binary)
Type G (graphics)
Type M (memo)

Type B en G werken wel, M niet. Ik krijg een JPG error 51 (komt toch in de buurt)

Ik heb 2 images op mijn form gezet. image1 bevat een jpg, image 2 niet.
Button1 kopieert image1 naar de DB.
Button2 kopieert van DB naar image2
Table1veld2 is het veld dat in paradox type G is.


procedure TForm1.Button1Click(Sender: TObject);
var tmpstream:tmemorystream; //
currentimage:timage; //
currentfield:tfield; //
begin
// save image
tmpStream := TMemoryStream.Create;
currentimage:=image1; //
currentfield:=table1veld2; //
try
TJPEGImage(CurrentImage.Picture.Graphic).SaveToStream(tmpStream);
table1.edit; //
TBlobField(CurrentField).LoadFromStream(tmpStream);
table1.Post; //
finally
tmpStream.Free;
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var tmpstream:tmemorystream; //
currentimage:timage; //
currentfield:tfield; //
jpg:tjpegimage; //
begin
// load image
tmpStream := TMemoryStream.Create;
currentimage:=image2; //
currentfield:=table1veld2; //
try
TBlobField(CurrentField).SaveToStream(tmpStream);
tmpStream.Position := 0;
jpg := TJPEGImage.Create;
jpg.LoadFromStream(tmpStream);
CurrentImage.Picture.Graphic := jpg;
finally
tmpStream.Free;
end;
end;

Acties:
  • 0 Henk 'm!

Anoniem: 14829

Topicstarter
MSSQL heeft 3 soortgelijke blob types: binary, image en text. Ik heb type image gebruikt. Ik zal 't ook nog 's met type binary proberen, maar dan nog blijft 't vaag:

Waarom werkt 't bij mij niet via een SaveToStream / LoadFromStream combinatie en wel via SaveToFile / LoadFromFile (de rest van de code is vrijwel ongewijzigd)? En dat terwijl binnen de VCL vrijwel alle file I/O via streams gaat? (bij TJPEGImage weet ik dat niet zeker, want de source van de jpeg unit is niet vrijgegeven)

Misschien maakt het verschil in databases ook wat uit: jij gebruikt Paradox, waarschijnlijk via de BDE? Ik gebruik MSSQL via een ADO connectie.

Acties:
  • 0 Henk 'm!

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 19:11

Tomatoman

Fulltime prutser

Anoniem: 14829 schreef op dinsdag 14 juni 2005 @ 23:11:
Waarom werkt 't bij mij niet via een SaveToStream / LoadFromStream combinatie en wel via SaveToFile / LoadFromFile (de rest van de code is vrijwel ongewijzigd)? En dat terwijl binnen de VCL vrijwel alle file I/O via streams gaat? (bij TJPEGImage weet ik dat niet zeker, want de source van de jpeg unit is niet vrijgegeven)
De source van de jpeg unit is wel degelijk vrijgegeven. Je vindt jpeg.pas bij de meeste Delphiversies op de installatie-cd onder 'bonus' of iets dergelijks. Hij wordt niet meegekopieerd tijdens de installatie van Delphi.

Een goede grap mag vrienden kosten.


Acties:
  • 0 Henk 'm!

Anoniem: 14829

Topicstarter
Ik kan jpeg.pas op geen van de 3 Delphi 2005 Enterprise CD's vinden...

Acties:
  • 0 Henk 'm!

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 19:11

Tomatoman

Fulltime prutser

Vreemd, bij mij staat hij er op de Delphi 2005 cd's ook niet op. Bij Delphi 7 staat hij op de cd onder \Info\Extras\Jpeg.

Check je mail.

Een goede grap mag vrienden kosten.


Acties:
  • 0 Henk 'm!

Anoniem: 146949

Anoniem: 14829 schreef op dinsdag 14 juni 2005 @ 23:11:
MSSQL heeft 3 soortgelijke blob types: binary, image en text. Ik heb type image gebruikt. Ik zal 't ook nog 's met type binary proberen, maar dan nog blijft 't vaag:

Waarom werkt 't bij mij niet via een SaveToStream / LoadFromStream combinatie en wel via SaveToFile / LoadFromFile (de rest van de code is vrijwel ongewijzigd)? En dat terwijl binnen de VCL vrijwel alle file I/O via streams gaat? (bij TJPEGImage weet ik dat niet zeker, want de source van de jpeg unit is niet vrijgegeven)

Misschien maakt het verschil in databases ook wat uit: jij gebruikt Paradox, waarschijnlijk via de BDE? Ik gebruik MSSQL via een ADO connectie.
Ja, Ik gebruik BDE met Delphi 7.

Overigens zat ik nog wat te kijken in de ADODB unit, Daar hebben ze een TADOBLOBSTREAM in gedefinieerd. Die zou ik nog eens proberen. Staat in de VCL help ook.

Acties:
  • 0 Henk 'm!

Anoniem: 14829

Topicstarter
TADOBlobStream en TBlobStream had ik ook al geprobeerd, maar zonder resultaat.
TADOBlobStream voegt overigens niet zo gek veel nuttigs toe: 't is een directe TMemoryStream afgeleide, met wat leuke properties voor data-aware gebruik (en ik gebruik per definitie geen data-aware controls).
't Enige waar 'ie voor mij nuttig zou kunnen zijn is bij blobs van het type 'text', omdat 'ie dan bij ReadBlobData de boel omzet van varOleStr naar een Pascal string, zodat Delphi's TBlobField ook weer blij is.
Maar bij images heb je daar niet zo gek veel aan... :)

Tomatoman, mail gecheckt, en dank je! Blij mee! :)

Acties:
  • 0 Henk 'm!

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 19:11

Tomatoman

Fulltime prutser

Ik weet me te herinneren dat JPEG's met de huidige implementatie nogal wat onverwachte effecten hebben. Wat je zou kunnen doen is de JPEG laden in een TImage en vervolgens het plaatje tonen als een TBitmap:
Delphi:
1
  Image.Picture.Assign(TJPEGImage);
of
Delphi:
1
2
3
4
5
6
7
with TBitmap.Create do
try
  Assign(ImageScreenshot.Picture.Graphic as TJPEGImage);
  // doe iets met de bitmap
finally
  Free;
end;


Wat ook het probleem zou kunnen zijn is dat TFiler de class TJPEGImage niet kent. Oplossing: registreren.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
unit MijnJPEGOplossing;

interface

uses
  jpeg;

implementation

initialization
  RegisterClasses([TJPEGImage]);
end.

Een goede grap mag vrienden kosten.

Pagina: 1