Blobs ins DB Speichern
-
Ich habe es jetzt so:
SQLComponents->SQLClientDataSet->Append(); TBlobField* blobField = dynamic_cast <TBlobField*> (SQLComponents->SQLClientDataSet->FieldByName("BLOB")); if (blobField == NULL) return; blobField->LoadFromFile(BrowserFile); SQLComponents->SQLClientDataSet->Post();
Wobei das "Table" aus der Beschreibung zu "SQLComponents->SQLClientDataSet"
geworden ist.Leider klappt es nicht.
Gibt es noch mehr zu beachten?
Ich habe in der Klasse SQLConnection folgende Komponenten:
SQLConnection
ClientDataSet
DataSourceDas ClientDataSet hat als DBConnection die SQL angegeben und das Property FetchOnDemand auf true. Das Property MasterSource ist leer. In der Pull- Downliste des Propertys (Objektinspector) ist aber auch kein Item zur Auswahl.
Wodrann könnte es noch liegen???
-
Clip schrieb:
Ich habe es jetzt so:
SQLComponents->SQLClientDataSet->Append(); TBlobField* blobField = dynamic_cast <TBlobField*> (SQLComponents->SQLClientDataSet->FieldByName("BLOB")); if (blobField == NULL) return; blobField->LoadFromFile(BrowserFile); SQLComponents->SQLClientDataSet->Post();
Gibt es noch mehr zu beachten?
Also mit dbExpress habe ich noch nicht wirklich gearbeitet, aber wenn ich den Hilfe-Text richtig verstehe, legst Du mit einem TSQLClientDataSet-Objekt ein Abbild eines Teils Deiner Tabelle im Haupt-Speicher ab, werkelst daran herum und schiebst es dann wieder zurück ... und zwar mit der Methode ApplyUpdates.
Clip schrieb:
Ich habe in der Klasse SQLConnection folgende Komponenten:
SQLConnection
ClientDataSet
DataSourceSQLConnection ist keine Klasse. Komponenten sind Klassen.
Und dann heißen die bei Borland TSQLConnection, TClientDataSet, T...
-
Ich habe den Code jetzt folgender maßen angepasst:
// var definitions AnsiString SQL = "select BLOB from TABLE where BLOBX = 1"; const AnsiString BrowserFile = "c:\\temp\\test.pdf"; AnsiString UserName = "Username"; AnsiString Password = "Password"; // Initialize Connections Settings SQLComponents->SQLConnection->Params->Values["User_Name"] = UserName.c_str(); SQLComponents->SQLConnection->Params->Values["Password"] = Password.c_str(); SQLComponents->SQLClientDataSet->CommandText = SQL.c_str(); // connect / activate SQLComponents->SQLConnection->Connected = true; SQLComponents->SQLClientDataSet->Active = true; // save Blob SQLComponents->SQLClientDataSet->Append(); TBlobField* blobField = dynamic_cast <TBlobField*> (SQLComponents->SQLClientDataSet->FieldByName("BLOB")); if (blobField == NULL) return; blobField->LoadFromFile(BrowserFile); SQLComponents->SQLClientDataSet->ApplyUpdates(-1); // disconnect / deactivate SQLComponents->SQLConnection->Connected = false; SQLComponents->SQLClientDataSet->Active = false;
gehen tut es aber leider immer noch nicht
Hat denn noch nie jemand mit einem ClientDataSet Blobs geschrieben? das muss doch gehen!
Aktuell wird immer bei ApplyUpdates() eine Exeption ausgelößt.
Weiß jemand rat??
-
Clip schrieb:
Aktuell wird immer bei ApplyUpdates() eine Exeption ausgelößt.
Also da fehlt schon mal das Post() und was für eine Exception (Typ, Message)?
-
Mit Post() gehts auch nicht.
Die Exeption ist "Message 208, could 'nt finde TABLE..."
Wobei es TABLE auf jedenfall gibt.
D.h. Zugriff auf die DB besteht, nur etwas ist noch falsch...
Ich habe es zwischen durch mal streng nach Borland Hilfe probiert:
SQLComponents->SQLClientDataSet->Edit(); TBlobField* blobField = dynamic_cast <TBlobField*> (SQLComponents->SQLClientDataSet->FieldByName("TABLE")); blobField->LoadFromFile(BrowserFile); TBlobStream *Stream1; TStream *Stream2; Stream1 = new TBlobStream(blobField, bmRead); Stream2 = SQLComponents->SQLClientDataSet->CreateBlobStream(SQLComponents->SQLClientDataSet->FieldByName("TABLE"), bmReadWrite); Stream2->CopyFrom(Stream1, Stream1->Size); SQLComponents->SQLClientDataSet->Post(); delete Stream1; delete Stream2;
Damit erhalte ich folgende Exeption:
"Im Projekt pSaveBlob ist eine Exception der Klasse IEnvalidCast aufgetreten. Meldung:'Ungültige Typumwandlung'"und zwar hier :
Stream1 = new TBlobStream(blobField, bmRead);
Leider gibt sich die Borland Hilfe an dieser Stelle sehr schwach, denn sie verwendet nicht initialisierte Variablen. Ich habe also im Header File von TBlobStream nachgeschaut, und da stand das ein BlobField als erstes Argument übergeben werden soll.
Hier mal die Borland Hilfe:
void __fastcall TForm1::Button1Click(TObject *Sender) { TBlobStream *Stream1; TStream *Stream2; Stream1 = new TBlobStream(Table1Notes, bmRead); try { ClientDataSet1->Edit(); // Andere Möglichkeit zum Erstellen eines BLOB-Streams Stream2 = ClientDataSet1->CreateBlobStream(ClientDataSet1->FieldByName("Remarks"), bmReadWrite); try { Stream2->CopyFrom(Stream1, Stream1->Size); ClientDataSet1->Post(); } __finally { delete Stream2; } } __finally { delete Stream1; } }
Ich finde ja mal hätte da noch reinschrieben können was "Table1Notes" sein soll...
egal.
Gerade habe ich noch eine andere Hilfe gefunden, ebenfalls zu CreateBlobStream:
void __fastcall TForm1::Button1Click(TObject *Sender) { TStream *Stream1, *Stream2; Stream1 = SQLDataSet1->CreateBlobStream(SQLDataSet1->FieldByName("Notes"), bmRead); try { ClientDataSet1->Edit(); Stream2 = ClientDataSet1->CreateBlobStream(ClientDataSet1->FieldByName("Remarks"), bmReadWrite); try { Stream2->CopyFrom(Stream1, Stream1->Size); ClientDataSet1->Post(); } __finally { delete Stream2; } } __finally { delete Stream1; } }
fast das selbe, aber eben doch anders....
Da soll jetzt mal noch einer schlau draus werden! Warum gibnt es zwei unterschiedliche Hilfen zu einer Methode?
Das eine ist die reguläre Hilfe, das andere die Referenzhilfe.
Ich werde mal eben nochmal den Referenzhilfscode ausprobieren.Danke aber schonmal wegen der ganzen Hilfe!
Ich dneke das ich gleich wieder da bin
-
geht alles irgentwie nicht ....
komisch ist, das nach
Stream2 = SQLComponents->SQLClientDataSet->CreateBlobStream(SQLComponents->SQLClientDataSet->FieldByName("BLOB"), bmReadWrite); Stream2->Write(Buffer, Bytes); int t = Stream2->Size;
t nicht die Größe des Bytes (Bytes des Buffers), sondern die Größe des BLOBS der bereits in der Datenbank ist hat.
Hat nich noch wer ne Idee was ich falsch machen könnte?
ich weiß nicht mehr weiter
-
Werfe mal einen Blick auf folgende Seiten:
http://www.bcbdev.com/faqs/faq94.htm
http://www.bridgespublishing.com/articles/issues/9809/How_can_I_use_BLOBs_with_C++Builder_.htm
-
Danke!
Ich habe den Code jetzt soweit:
AnsiString SQL1 = "select BLOB from TABLE where BLOBX ="; AnsiString DokuX = Edit1->Text; const AnsiString BrowserFile = "c:\\temp\\test.gif"; AnsiString Query1 = SQL1 + DokuX; AnsiString UserName = "Username"; AnsiString Password = "Password"; //Klasse mit benötigten SQL Komponenten if (SQLComponents == NULL) { SQLComponents = new TSQLComponents(NULL); } SQLComponents->SQLConnection->Params->Values["User_Name"] = UserName.c_str(); SQLComponents->SQLConnection->Params->Values["Password"] = Password.c_str(); // verbindung herstellen SQLComponents->SQLClientDataSet->CommandText = Query1.c_str(); SQLComponents->SQLConnection->Connected = true; SQLComponents->SQLClientDataSet->Active = true; // Feld bearbeiten SQLComponents->SQLClientDataSet->Edit(); TBlobField * blobField = dynamic_cast <TBlobField *> (SQLComponents->SQLClientDataSet->FieldByName("BLOB")); blobField->LoadFromFile(BrowserFile); TStream *Stream2; Stream2 = SQLComponents->SQLClientDataSet->CreateBlobStream(SQLComponents->SQLClientDataSet->FieldByName("BLOB"), bmReadWrite); blobField->SaveToStream(Stream2); SQLComponents->SQLClientDataSet->Post(); SQLComponents->SQLClientDataSet->ApplyUpdates(-1); // Disconnect SQLComponents->SQLConnection->Connected = false; SQLComponents->SQLClientDataSet->Active = false; delete[] Buffer; delete Feld; delete SQLComponents;
Das neue ist, daß jetzt mal keine Typenexepction oder ähnliches kommen, sondern scheinbar eine Nachricht der Datenbank (oder der Client Datenmenge)
Und zwar:
"Im Projekt pSaveBlob ist eine Exception der Klasse EDataBaseError" aufgetreten. Meldung:'Datensatz nicht gefunden; Es wurde kein Schlüssel angegeben'"Das kling für mich nach einem (kleinem) Vortschritt!
Jetzt scheint "nur" noch der SQL String nicht ganz richtig zu sein.Den wollt eich dann auch promt in ein INSERT statement umwandeln. Allerdings kenne ich vor dem setzten von "SQLComponents->SQLClientDataSet->CommandText" ja den Datensatzt garnicht, bzw. habe ihn nicht verfügbar. Und connecten ohne CommandText geht nicht. Zudem benutze ich doch den CreateBlobStream Befehl um nicht an Insert Bergänzungen bei der Blobgröße hängen zu bleiben.
komisch das alles....
-
Kann es sein, das in Deiner Tabelle keine Primärschlüsselspalte existiert?
-
Ne!
Die Primärschlusselspalte ist "BLOBX"
-
Damit es hier icht zu langweilig wird, und es auch mal etwas positives zu erzählen gibt. Ichhabe zwar immer nich nicht mein Problem mit dem Speichern gelöst, aber einen recht einfachen Weg gefunden BLOBS aus einer DB in Files zu speichern.
AnsiString SQL1 = "select BLOB from TABLE where BLOBX ="; AnsiString UserName = "Username"; AnsiString Password = "Password"; AnsiString DokuX = Edit1->Text; AnsiString Path = "c:\\temp\\"; AnsiString File ="\0"; AnsiString Query1 = SQL1 + DokuX; if (SQLComponents == NULL) { SQLComponents = new TSQLComponents(NULL); } SQLComponents->SQLConnection->Params->Values["User_Name"] = UserName.c_str(); SQLComponents->SQLConnection->Params->Values["Password"] = Password.c_str() // Blob Auslesen!! SQLComponents->SQLClientDataSet->CommandText = Query1.c_str(); SQLComponents->SQLConnection->Connected = true; SQLComponents->SQLClientDataSet->Active = true; SQLComponents->SQLClientDataSet->SaveToFile(File,dfBinary); SQLComponents->SQLConnection->Connected = false; SQLComponents->SQLClientDataSet->Active = false; delete SQLComponents;
Ich habe dann natürlich gleich die Methode LoadFromFile() ausprobiert. ( keine Ahnung warum ich die nicht früher gefunden hatte ...) Exeptions gab es zwar nicht, Änderungen in der DB aber auch nicht
In der Hilfe stand zusätzlich folgender denkwürdiger Satzt zu:
void __fastcall LoadFromFile(const AnsiString FileName = "");"Hinweis: Die in FileName angegebene Datei muß Daten enthalten, die zuvor durch einen Aufruf der Datenmengenmethode SaveToFile gespeichert wurden"
Toll!
Heißt das, daß ich mit der Funktion nur Files in die DB schreiben darf die da eigentlich schon drinne liegen?? Wäre ja super ...... *mit dem kopf vor die wand schlag*
-
Dein Problem ist also immer noch die Save-Funktion. Hm. Irgendwie ist das da SELECT-Statement noch nicht das richtige. Leider habe ich keine Zeit mich so richtig damit zu beschäftigen. Vielleich ist ja hier noch jemand anderes, der sich damit auskennt.
-
Ich glaube ich werde es jetzt einmal streng nach "Vorschrift" aus dem Entwickler Forum probieren:
void __fastcall TForm1::SaveImageBtnClick(TObject *Sender) { if (OpenPictureDlg->Execute() == false) return; Table->Active = true; Table->Append(); Table->FieldByName("Name")->AsString = ExtractFileName(OpenPictureDlg->FileName); TBlobField* blobField = dynamic_cast <TBlobField*> (Table->FieldByName("PICTURE")); if (blobField == NULL) return; blobField->LoadFromFile(OpenPictureDlg->FileName); Table->Post(); }
Allerdings weiß ich nicht was "Table" für ein Objekt sein soll
Was könnte das sein?
-
Clip schrieb:
Allerdings weiß ich nicht was "Table" für ein Objekt sein soll
Was könnte das sein?Hast Du schon geklärt, ob Dein Problem überhaupt mit dem Blob zusammenhängt?
Versuch doch erstmal, irgend etwas anderes als ein Blob abzuspeichern ... geht das?
-
Ja, das geht völlig Problemlos.
Prügel mich gerade mit SQLTable und SQLQuery. Sah erst alles ganz toll aus.
Dann habe ich bemerkt, dass CanModify von SQLTable immer auf false steht......Ich glaub ich geh wieder mir Papierflugzeugen spielen oder so...
Es muss doch eine Möglichkeit geben Blobs mit dbExpress zu speichern!
Ich werds weiter probieren!
-
Soooooooo
also Blobs aus der DB anzeigen:
AnsiString SQL1 = "select BLOB from TABLE where BLOBX ="; AnsiString SQL2 = "select * from BLOBINFO where BLOBX ="; AnsiString UserName = "username"; AnsiString Password = "passwort"; AnsiString DokuX = Edit1->Text; AnsiString Path = "c:\\temp\\"; AnsiString File ="\0"; AnsiString Query1 = SQL1 + DokuX; if (SQLComponents == NULL) { SQLComponents = new TSQLComponents(NULL); } // Username & Passwort setzten SQLComponents->SQLConnection->Params->Values["User_Name"] = UserName.c_str(); SQLComponents->SQLConnection->Params->Values["Password"] = Password.c_str(); // DateiINfos Auslesen!! SQLComponents->SQLClientDataSet->CommandText = Query2.c_str(); SQLComponents->SQLConnection->Connected = true; SQLComponents->SQLClientDataSet->Active = true; File = SQLComponents->SQLClientDataSet->FieldByName("DATEINAME")->AsString; File = Path + File; SQLComponents->SQLConnection->Connected = false; SQLComponents->SQLClientDataSet->Active = false; // Connecten SQLComponents->SQLClientDataSet->CommandText = Query1.c_str(); SQLComponents->SQLConnection->Connected = true; SQLComponents->SQLClientDataSet->Active = true; // Blob wird ausgelesen TBlobField* blobField = dynamic_cast <TBlobField*> (SQLComponents->SQLClientDataSet->FieldByName("BLOB")); if (blobField == NULL) return; blobField->SaveToFile(File); // disconnect SQLComponents->SQLConnection->Connected = false; SQLComponents->SQLClientDataSet->Active = false; // File Anzeigen WideString WFile; WFile = WideString(File); CppWebBrowser1->Navigate(WFile, NULL, NULL, NULL, NULL); delete SQLComponents;
Blobs in die DB schreiben:
AnsiString SQL1 = "select * from TABLE where BLOBX ="; AnsiString DokuX = Edit1->Text; const AnsiString BrowserFile = "c:\\temp\\test.pdf"; AnsiString Query1 = SQL1 + DokuX; AnsiString Query2 = SQL2 + DokuX; AnsiString UserName = "username"; AnsiString Password = "passwort"; // Hilfsvariablen für Locate() Variant SearchKey = DokuX; TLocateOptions Opts; //Klasse mit benötigten SQL Komponenten if (SQLComponents == NULL) { SQLComponents = new TSQLComponents(NULL); } // Username & Passwort setzten SQLComponents->SQLConnection->Params->Values["User_Name"] = UserName.c_str(); SQLComponents->SQLConnection->Params->Values["Password"] = Password.c_str(); // Query setzten SQLComponents->SQLClientDataSet->CommandText = Query1.c_str(); // connecten SQLComponents->SQLConnection->Connected = true; SQLComponents->SQLClientDataSet->Active = true; // Datensatzt wird ausgewählt Opts.Clear(); Opts << loCaseInsensitive; SQLComponents->SQLClientDataSet->Locate("BLOBX", SearchKey, Opts); // Edit Modus SQLComponents->SQLClientDataSet->Edit(); // Blob wird in den selektierten Datensatzt copiert. TBlobField* blobField = dynamic_cast <TBlobField*> (SQLComponents->SQLClientDataSet->FieldByName("BLOB")); if (blobField == NULL) return; blobField->LoadFromFile(BrowserFile); // Änderungen werden an die DB übertragen SQLComponents->SQLClientDataSet->ApplyUpdates(-1); // disonnect SQLComponents->SQLConnection->Connected = false; SQLComponents->SQLClientDataSet->Active = false; delete SQLComponents;
@admins, evtl. ist das in gekürzter Form ja was für die die FAQs?
-
Äääh ... und was war letztlich das Problem?
-
Zum laden von Daten reicht scheinbar eine Select Anweisung im CommandText um auf ein bestimmtes Datenfeld zuzugrifen. Dies reicht aber nicht wenn etwas gespeichert werden soll. Daher muss nocheinmal der Datensatz ausgewählt werden.
Variant SearchKey = DokuX; TLocateOptions Opts; Opts << loCaseInsensitive; SQLComponents->SQLClientDataSet->Locate("BLOBX", SearchKey, Opts);
Das ist eigentlich alles.
und Vielen Dank für Deine Hilfe noch!!!!!
-
Soviel Aufwand für eine Funktionalität, von deren Anwendung üblicherweise abgeraten wird.
Wenn du das in der FAQ sehen möchtest solltest du aber auch noch den Code deiner SQLComponents-Klasse dazupacken, damit man das 1:1 nachvollziehen kann.
-
Soviel Aufwand für eine Funktionalität, von deren Anwendung üblicherweise abgeraten wird
Warum denn?
Die spezielle Umsetzung die ich jetzt verwendet habe, oder Blobs generell?