[Delphi/Interbase] TIBBlobStream help klopt niet

Pagina: 1
Acties:

  • Paul
  • Registratie: September 2000
  • Laatst online: 15-09 19:17
ik ben momenteel bezig met het maken van een soort mail-client. Daarvoor wil ik de body van de mail en de attachments als BLOB opslaan in Interbase. De database heb ik al, alleen het vullen en het uitlezen wil niet.

Ik probeerde het als eerste met een TIBBLobStream. Daarbij hoort het volgende example, wat ik overigens letterlijk en ook met een hoop geklooi heb geprobeerd:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
procedure TForm1.Button1Click(Sender: TObject);

var
  Stream1, Stream2 : TIBBlobStream;
begin
  Stream1 := TIBBlobStream.Create(IBTable1Notes, bmRead);
  try
    IBTable2.Edit;
    { heres a different way to create a blob stream } 
    Stream2 := IBTable2.CreateBlobStream(IBTable2.FieldByName
('Remarks'), bmReadWrite);
    try
    Stream2.CopyFrom(Stream1, Stream1.Size);
    IBTable2.Post;
    finally
    Stream2.Free;
    end;
  finally
    Stream1.Free;

  end;
end;

Echter: methode 1 zegt: too many actual parameters. De create van TIBBlobStream bevat GEEN parameters volgens de compiler.
De tweede methode zegt dat TIBBLobStream en TStream incompatible types zijn, terwijl TIBBlobStream een directe descendant van TStream is :?
Ik kan het oplossen door van TIBBlobStream een TStream te maken, maar dan moet ik zelf een SafeToFile en een LoadFromFile maken. Dat had ik gedaan (sort of, voor mijn situatie, via een TFileStream en dan een copyFrom), maar zodra ik dat uit probeer te lezen, dan krijg ik alleen maar wat vage tekst van 5 karakters lang?
Dat is dan dezelfde tekst als wanneer ik in WISQL de statement 'SELECT body FROM mails' uitvoer...
Hoe krijg ik dit dus werkend?

De body kan ik eventueel vervangen door een varchar van 65K of zo (als dat kan, weet ik zo nog niet), maar dan zit ik nog steeds met de attachments.
Ook kan ik wel een create zonder parameters meegeven aan TIBBlobStream, en dan zelf proberen in te vullen welke tabel en welke row de benodigde blob is, maar meer (niet read only) properties dan 'Database', 'Transaction', 'Mode' en BlobID zijn er niet, en aangezien er geen type opgegeven wat voor type BlobID is, weet ik niet wat ik daar mee moet doen. Moet ik daar die vage korte string geven die ik krijg bij mijn select?

Wet iemand hoe ik dit oplos? Het liefste op de officiele manier natuurlijk :)

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock


Verwijderd

Ik denk dat ze wat bugs uit interbase hebben gehaald maar nog niet de help-file hebben aangepast :)

Ik heb verder weinig ervaring met interbase, maar volgens mij is IBTable1Notes van het type TBlobStream, of niet? Kun je dan niet gewoon IBTable1Notes.SaveToFile(MyFileName); doen?

Stream2 moet idd van het type TStream zijn [is eigenlijk TIBDSBlobStream] maar hiermee kun je dus niet standaard een SaveToFile mee doen.

  • Paul
  • Registratie: September 2000
  • Laatst online: 15-09 19:17
Op dinsdag 22 januari 2002 22:30 schreef DiFool het volgende:
Ik denk dat ze wat bugs uit interbase hebben gehaald maar nog niet de help-file hebben aangepast :)

Ik heb verder weinig ervaring met interbase, maar volgens mij is IBTable1Notes van het type TBlobStream, of niet? Kun je dan niet gewoon IBTable1Notes.SaveToFile(MyFileName); doen?
IBTable1Notes is een TField... IBTable2.FieldByName levert ook een TField op, dus in wezen zijn die twee exact gelijk, alleen dan dat TIBBlobStream.Create geen parameters (meer? |:( ) accepteerd.
Stream2 moet idd van het type TStream zijn [is eigenlijk TIBDSBlobStream] maar hiermee kun je dus niet standaard een SaveToFile mee doen.
Maar is een directe descendant van een klasse dan niet compatible met die klasse? Als iets een TStream opleverd dan behoor ik die toch gewoon in die descendant te kunnen gooien? In de help staat letterlijk bij hierarchie:
code:
1
2
3
4
5
TObject
  |
TStream
  |
TIBBlobStream

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock


Verwijderd

DiFool: Ik heb verder weinig ervaring met interbase, maar volgens mij is IBTable1Notes van het type TBlobStream, of niet? Kun je dan niet gewoon IBTable1Notes.SaveToFile(MyFileName); doen?
Oeps, typ fout, ik bedoelde dat IBTable1Notes van het type TBlobField is....
pnieuwkamp:
IBTable1Notes is een TField... IBTable2.FieldByName levert ook een TField op, dus in wezen zijn die twee exact gelijk, alleen dan dat TIBBlobStream.Create geen parameters (meer?) accepteerd.
Lijkt me sterk dat als je een kolom design-time toevoegt, hij een TField is. Een var dat echt [dwz niet TBlobField ofzo] van het type TField is kun je niet gebruiken. [Dit type wordt bv. gebruikt om met functies types te retourneren, waarvan je compile-time nog niet weet wat het type ervan is [TIntegerField, TStringField, etc]. Wat je wel weet is dat het iig een TField is]

Dit moet dus IMHO een TBlobField oid zijn, of je hebt hem fout toegevoegd.

Als veld 'memo' van het type ftBlob is dan zal IBTable1.FieldByName('memo') een TBlobField retourneren; je zult hem wel moeten type-casten voor gebruik, omdat de compiler denkt dat je een TField terugkrijgt, dus
code:
1
  TBlobField(IBTable1.FieldByName('memo')).SaveToFile(MyFileName);

Eventueel kun je nog testen:
code:
1
2
3
4
5
6
7
var
  Fld: TField;
begin
  Fld := IBTable1.FieldByName('memo');
  if Fld is TBlobField then 
    TBlobField(Fld).SaveToFile(MyFileName);
end;
pnieuwkamp:
Maar is een directe descendant van een klasse dan niet compatible met die klasse? Als iets een TStream opleverd dan behoor ik die toch gewoon in die descendant te kunnen gooien? In de help staat letterlijk bij hierarchie:
code:
1
2
3
4
5
TObject
  |
TStream
  |
TIBBlobStream
Nee, het is juist precies andersom. Je kunt iets down-casten maar niet up-casten. Dus als Blob van het type TIBBlobStream is, kun je wel TStream(Blob).Size doen maar als Str van het type TStream is kun je niet TIBBlobStream(Str).DBHandle oid dergelijk doen.

Je kunt ook dit doen:
code:
1
2
3
4
functie CreateField: TField;
begin
  Result := TBlobField.Create(nil);
end;

Hier down-cast je de TBlobField naar een TField in de CreateField. Na de functie aanroep kun je het resultaat weer up-casten naar een TBlobField, omdat het resultaat van dat type is. Dit is dus wat ongeveer in de FieldByName method gebeurd.

  • Paul
  • Registratie: September 2000
  • Laatst online: 15-09 19:17
Op woensdag 23 januari 2002 01:12 schreef DiFool het volgende:

[..]

Oeps, typ fout, ik bedoelde dat IBTable1Notes van het type TBlobField is....
[..]

Lijkt me sterk dat als je een kolom design-time toevoegt, hij een TField is. Een var dat echt [dwz niet TBlobField ofzo] van het type TField is kun je niet gebruiken. [Dit type wordt bv. gebruikt om met functies types te retourneren, waarvan je compile-time nog niet weet wat het type ervan is [TIntegerField, TStringField, etc]. Wat je wel weet is dat het iig een TField is]

Dit moet dus IMHO een TBlobField oid zijn, of je hebt hem fout toegevoegd.
Wat ik bedoel is, dat de procedureomachrijving TIBBlobStream.Create(veld: TField; access: (iets)); is... Dus dat hij iets van het type TField als parameter accepteerd.

Tijdens designtime kolommen toevoegen aan het DBGrid gaat niet echt automagisch, aangezien ik de databasename uit een (dbase) database haal (don't ask :( )
Als veld 'memo' van het type ftBlob is dan zal IBTable1.FieldByName('memo') een TBlobField retourneren; je zult hem wel moeten type-casten voor gebruik, omdat de compiler denkt dat je een TField terugkrijgt, dus
code:
1
  TBlobField(IBTable1.FieldByName('memo')).SaveToFile(MyFileName);

Eventueel kun je nog testen:
code:
1
2
3
4
5
6
7
var
  Fld: TField;
begin
  Fld := IBTable1.FieldByName('memo');
  if Fld is TBlobField then 
    TBlobField(Fld).SaveToFile(MyFileName);
end;
Dank je, dat ga ik dan zeker proberen. Al weet ik niet ef oje een FIELD nu kunt saven? Dacht dat het daarvoor een stream moest zijn?
[..]

Nee, het is juist precies andersom. Je kunt iets down-casten maar niet up-casten. Dus als Blob van het type TIBBlobStream is, kun je wel TStream(Blob).Size doen maar als Str van het type TStream is kun je niet TIBBlobStream(Str).DBHandle oid dergelijk doen.
Nee, dat snap ik. Is ook logisch, want de kans is dan levensgroot dat die procedure helemaal niet bestaat in de, euh, parent is het niet, upperclass?
Wat ik bedoel: als iets als bijvoorbeeld procedure foo(var bar: TStream); dus een TStream verwacht, dan kan ik toch gewoon een TFileStream oid meegeven? Ik bedoel, de verwachtte procedures zijn gewoon overgeerfd, dus dat mag absoluut geen probleem zijn :?
Je kunt ook dit doen:
code:
1
2
3
4
functie CreateField: TField;
begin
  Result := TBlobField.Create(nil);
end;

Hier down-cast je de TBlobField naar een TField in de CreateField. Na de functie aanroep kun je het resultaat weer up-casten naar een TBlobField, omdat het resultaat van dat type is. Dit is dus wat ongeveer in de FieldByName method gebeurd.
Hmm. Ik zal eerst het vorige stukje code proberen, maar dit is misschien ook wel wat. Want dat lukt dus waarschijnlijk ook met andere componenten dan alleen TFields :)

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock


Verwijderd

Oke volgens mij zijn we er bijna uit :)
pnieuwkamp: Wat ik bedoel is, dat de procedureomachrijving TIBBlobStream.Create(veld: TField; access: (iets)); is... Dus dat hij iets van het type TField als parameter accepteerd.
Ja, dat klopt, alleen is die constructor Create zo niet gedefiniëerd. De help-file is fout.
Tijdens designtime kolommen toevoegen aan het DBGrid gaat niet echt automagisch, aangezien ik de databasename uit een (dbase) database haal (don't ask :( )
Volgens mij bedoel ik iets anders dan jij; als ik een var met de naam IBTable1Notes zie, ga ik ervan uit dat die met - rechtermuis knop op IBTable1|Fields Editor|Rechtermuis knop op de fields editor|Select all fields - is toegevoegd. Bedoel jij dat ook?
Dank je, dat ga ik dan zeker proberen. Al weet ik niet ef oje een FIELD nu kunt saven? Dacht dat het daarvoor een stream moest zijn?
var van type TField kun je niet saven, var van type TBlobField wel; IBTable.FieldByName retourneert een TBlobField [Dus geen TIBBlobField] als het datatype van die kolom een blob is.
Wat ik bedoel: als iets als bijvoorbeeld procedure foo(var bar: TStream); dus een TStream verwacht, dan kan ik toch gewoon een TFileStream oid meegeven? Ik bedoel, de verwachtte procedures zijn gewoon overgeerfd, dus dat mag absoluut geen probleem zijn :?
Is geen probleem.
Hmm. Ik zal eerst het vorige stukje code proberen, maar dit is misschien ook wel wat. Want dat lukt dus waarschijnlijk ook met andere componenten dan alleen TFields :)
Oh, die laatste code doet niets nuttigs, was gewoon een voorbeeldje :)

  • Paul
  • Registratie: September 2000
  • Laatst online: 15-09 19:17
Op woensdag 23 januari 2002 23:37 schreef DiFool het volgende:
Oke volgens mij zijn we er bijna uit :)
Ja, lijkt me ook. Tnx
[..]
Ja, dat klopt, alleen is die constructor Create zo niet gedefiniëerd. De help-file is fout.
[..]

Volgens mij bedoel ik iets anders dan jij; als ik een var met de naam IBTable1Notes zie, ga ik ervan uit dat die met - rechtermuis knop op IBTable1|Fields Editor|Rechtermuis knop op de fields editor|Select all fields - is toegevoegd. Bedoel jij dat ook?
Ik moet eerlijk bekennen dat ik niet helemaal wist waar ze die vandaan hadden (al die code was van het voorbeeld uit de help). Al onze databases tabellen zijn in DBASE formaat (niet mijn idee), en volgens onze opzet valt er tijdens de designtime weinig te doen. We createn alle TTable's zelf, dus zonder VCL oid. Alleen dit projectje toevallig iets met interbase
[..]

var van type TField kun je niet saven, var van type TBlobField wel; IBTable.FieldByName retourneert een TBlobField [Dus geen TIBBlobField] als het datatype van die kolom een blob is.
Aha. Weer wat geleerd. Omdat er achter de functieomschrijving TField stond, ging ik er vanuit dat je een TField terugkreeg, maar blijkbaar niet :?
[..]

Is geen probleem.
Alleen jammer dan dat Stream2 uit het voorbeeld exact dat doet (en ja, de omschrijving van TBDEDataSet.CreateBlobStream klopt), en er toch een compilerfout komt (incompatible types)...
[..]

Oh, die laatste code doet niets nuttigs, was gewoon een voorbeeldje :)
Bedankt alvast, ik ga het morgen vandaag proberen *D

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock


Verwijderd

pnieuwkamp:
Alleen jammer dan dat Stream2 uit het voorbeeld exact dat doet (en ja, de omschrijving van TBDEDataSet.CreateBlobStream klopt), en er toch een compilerfout komt (incompatible types)...
TIBDataSet.CreateBlobStream retourneert een TStream [eigenlijk TIBDSBlobStream] dus dit moet goed gaan:
code:
1
2
3
4
5
6
7
8
9
10
11
var 
  Stream: TStream;
begin
  Stream := IBTable2.CreateBlobStream(IBTable2.FieldByName('Remarks'), 
    bmReadWrite);
  try
    { Doe iets met Stream }
  finally
    Stream.Free;
  end;
end;

Maar ook daar klopt het help-file voorbeeld niet inderdaad.

De Stream heeft wel een protected property FBlobStream van het type TIBBlobStream, dus het volgende zou moeten lukken:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
uses
  IBBlob, IBCustomDataSet;

[..]

type
  { Dit type gebruik je om bij de protected vars van het
    type TIBDSBlobStream te komen }
  THackStream = class(TIBDSBlobStream);
var
  Stream: TStream;
begin
  Stream := IBTable2.CreateBlobStream(IBTable2.FieldByName('Remarks'), 
    bmReadWrite);
  try
    THackStream(Stream).FBlobStream.SaveToFile(MyFileName);
  finally
    Stream.Free;
  end;
end;

Niet getest, maar zou moeten werken :)

  • Paul
  • Registratie: September 2000
  • Laatst online: 15-09 19:17
Mog ik je nog een keertje hartelijk bedanken :)
Ik heb vandaag de hele dag bij klanten gezeten, dus programmeren is niet gelukt, maar zo moet het nu onderhand toch wel lukken :)

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock

Pagina: 1