Blobs ins DB Speichern



  • Hallo!

    Ich möchte einen Blob (File) in einer Datenbank speichern.
    Ich habe zwar die Suchfunktion ausgiebig gebutzt, aber ich konnte leider nicht wirklich finden was ich gesucht habe.
    Daher mein Frage:
    Wie mach eich das?

    Bis jetzt habe ich

    AnsiString SQL1 = "select BLOB from TABELLE where ID =";
      AnsiString DokuX = Edit1->Text;
      AnsiString File = "\0";
      AnsiString Path = "\0";
      AnsiString BrowserFile = "\0";
      int iFileHandle = 0;
      int Bytes = 0;
      AnsiString Query1 = SQL1 + DokuX;
      TBlobField * Feld = new TBlobField(NULL);
      TStream * Stream;
      AnsiString UserName = "name";
      AnsiString Password = "password";
    
      // Testfile
      BrowserFile = "c:\\temp\\test.pdf";
    
      // Handel mit dem die Datei angesprochen werden kann
      iFileHandle = FileOpen(BrowserFile, fmOpenRead);
      // Länge der Datei
      Bytes = FileSeek(iFileHandle,0,2);
      // wieder an den Fileanfang springen
      FileSeek(iFileHandle,0,0);
    
      // Datei in einen Buffer schreiben
      char * Buffer = new char[Bytes];
      FileRead(iFileHandle, Buffer, Bytes);
      FileClose(iFileHandle);
    
      //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();
    
      // Blob speichern!!
      SQLComponents->SQLClientDataSet->CommandText = Query1.c_str();
      SQLComponents->SQLConnection->Connected = true;
    	SQLComponents->SQLClientDataSet->Active = true;
    
      // Edit Modus öffnen
      SQLComponents->SQLClientDataSet->Edit();
      // Zeiger auf DB Feld
      Stream = SQLComponents->SQLClientDataSet->CreateBlobStream(SQLComponents->SQLClientDataSet->FieldByName("BLOB"), bmWrite);
      // Stream in ClientDataSet schreiben
      Stream->Write(Buffer, Bytes);
      // CDS Posten
      SQLComponents->SQLClientDataSet->Post();
    
      // Disconnect
      SQLComponents->SQLConnection->Connected = false;
    	SQLComponents->SQLClientDataSet->Active = false;
    
      delete[] Buffer;
      delete Feld;
      delete SQLComponents;
    

    Das ganze läuft auch Fehlerfrei ab, allerdings wird nichts in die Tabelle geschrieben.
    Was mich selber stutzig gemacht hat ist, das ich ein select Befehl absetzte um später in das Blobfeld zu schreiben. Allerdings generiere ich mit dem select Befehl ja nur einen Zeiger auf ein DB- Feld und schreibe später via CreateBlobStream in das Feld. Ich habe es mal mit Update oder Instert probiert, da kamen aber immer Exeptions.
    Hat jemand n Idee?

    Danke!



  • Clip schrieb:

    Hallo!

    Ich möchte einen Blob (File) in einer Datenbank speichern.

    Clip,
    schau mal hier rein: http://www.entwickler-forum.de/webx?13@167.tDmpaEpWdnG.7@.1dd057a8/0



  • 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
    DataSource

    Das 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
    DataSource

    SQLConnection 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 😞





  • 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!!!!!


Anmelden zum Antworten