Laden...

SQL Server: Problem beim lesen von BLOB-Werten

Erstellt von wcseller vor 8 Jahren Letzter Beitrag vor 8 Jahren 3.155 Views
W
wcseller Themenstarter:in
196 Beiträge seit 2008
vor 8 Jahren
SQL Server: Problem beim lesen von BLOB-Werten

verwendetes Datenbanksystem: MS SQL (2012 und 2014)

Hallo,

ich stehe vor einem Problem, bei dem ich mir nicht mehr zu helfen weiß, auch Google etc. konnte mir leider nicht helfen.

Ich habe eine Tabelle Notizen, in der es eine Spalte des Typs 'Image' (die Datenbank ist schon älter, deshalb wurde hier noch mit 'Image' gearbeitet) mit dem Namen 'Datenfeld' gibt. In diesem Datenfeld sind Blobs/Streams/Binärdaten beliebiger Dateien gespeichert (PDF, DOC etc.). Dieses Datenfeld möchte ich auslesen um den Inhalt wieder als Datei speichern zu können. Der Inhalt von Datenfeld ist ein UTF8 codierter Stream. Soweit so gut.

Ich habe nun folgende Varianten ausprobiert, die auf einem MS SQL Server 2014 auch alle problemlos funktionieren:

Variante 1:

using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
     connection.Open();
     using (SqlCommand command = new SqlCommand("select Notiznr, Datenfeld from Notizen where Notiznr = 1000", connection))
     {
          using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
          {
              while (reader.Read())
              {
                   int fieldpos = reader.GetOrdinal("Datenfeld");
                   if (reader.IsDBNull(fieldpos))
                         break;

                   byte[] daten = new byte[]{};
                   using (MemoryStream mem = new MemoryStream())
                   {
                       using (Stream output = reader.GetStream(fieldpos))
                       {
                           output.CopyTo(mem);
                           daten = mem.ToArray();
                       }
                   }
              }
          }
      }

      connection.Close();
}

Variante 2:

using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
     connection.Open();
     using (SqlCommand command = new SqlCommand("select Notiz, Datenfeld from Notizen where Notiznr = 1000", connection))
     {
          using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
          {
              while (reader.Read())
              {
                   int fieldpos = reader.GetOrdinal("Datenfeld");
                   if (reader.IsDBNull(fieldpos))
                         break;

                   long totalbytes = reader.GetBytes(fieldpos, 0, null, 0, 0);
                   if (totalbytes < 1)
                       break;

                   long bytesgelesen = 0;
                   byte[] daten = new byte[totalbytes];
                   long startpos = 0;
                   while (bytesgelesen < totalbytes)
                   {
                       bytesgelesen += reader.GetBytes(fieldpos, startpos, daten, (int)startpos, 1024);  // tried with several block sizes...
                       startpos += 1024;
                   }
               }
          }
      }

      connection.Close();
}

Variante 3:

using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
     connection.Open();
     using (SqlCommand command = new SqlCommand("select Datenfeld from Notizen where Notiznr = 1000", connection))
     {
         byte[] daten = (byte[])command.ExecuteScalar();
     }
     connection.Close();
}

und Variante 4:

using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
     connection.Open();
     using (SqlCommand command = new SqlCommand("select Notiznr, Datenfeld from Notizen where Notiznr = 1000", connection))
     {
          using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
          {
              while (reader.Read())
              {
                   int fieldpos = reader.GetOrdinal("Datenfeld");
                   if (reader.IsDBNull(fieldpos))
                         break;

                   byte[] daten = (byte[])reader[fieldpos];
              }
          }
      }
      connection.Close();
}

Nun habe ich die exakt gleiche Datenbank mit zwei verschiedenen MS SQL Server 2012 Installationen ausprobiert und erhalte dort statt der erwünschten Daten immer 20 Bytes, die den Text 'OLE-Objekt' repräsentieren (UTF8, deswegen 20 Bytes). Ich habe auch schon versucht statt der Sql-Klassen mit OleDB zu arbeiten (funktioniert auf dem MS SQL Server 2014 wieder problemlos) aber bekomme immer das gleiche Ergebnis.

Weiß jemand Rat?

Gruß,
wcseller

3.825 Beiträge seit 2006
vor 8 Jahren

Die Datenbankspalte kannst Du direkt einen Byte-Array zuweisen.

Also

 byte[] daten = (byte[])reader["Datenfeld"];

Hast Du ja in Deiner Variante 4 auch geschrieben. Das sollte immer funktionieren.

Das klappt bei uns mit SQL Server 2005, 2008, 2012 und 2015.

OleDB ist eine ganz schlechte Idee, da klappt fast nichts und es ist sehr langsam dazu.

Bei Dir scheint es sich ja um OLE-Objekte zu handeln, da ist das Lesen aufwändiger :

Saving OLE Object as Image Datatype in SQL

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

502 Beiträge seit 2004
vor 8 Jahren

Wie BerndFfm schon geschrieben hat, klappt das mit dem direkten Lesen von byte[] meines wissens nach in allen SQL-Server Versionen. Selbiges gilt auch für das Schreiben der Daten - dort kann direkt ein byte[] in einen Abfrage-Parameter gepackt werden.

Das bringt mich zur eigentlichen Frage: Bist Du sicher, dass die Daten überhaupt richtig in die DB geschrieben werden? Oder liegt da vielleicht schon das grundlegende Problem?

Bart Simpson

Praxis ist wenn alles funktioniert und keiner weiss warum.
Theorie ist wenn man alles weiss, aber nichts funktioniert.

Bei uns wird Theorie und Praxis vereint: Nichts funktioniert und keiner weiss warum...

F
10.010 Beiträge seit 2004
vor 8 Jahren

Bist Du sicher, dass die Daten überhaupt richtig in die DB geschrieben werden? Oder liegt da vielleicht schon das grundlegende Problem?

Denke auch das das Problem da liiegt.

Denn Binärdaten liegen niemals in UTF8 vor, denn es sind Binärdaten.
Und schreibt man Binärdaten mit den Stringfunktionen ( UTF8 z.b. ) wird immer nach einem 0x00 das Ende signalisiert.

W
wcseller Themenstarter:in
196 Beiträge seit 2008
vor 8 Jahren

Vielen Dank für die 'Anteilnahme'.

Für das Schreiben der Daten war/ist eine ältere, in VB6 geschriebene Anwendung verantwortlich. Ich greife auf die Datenbank nur lesend zu (Quelltext steht mir leider nicht zur Verfügung).

Zum Testen meiner Anwendung habe ich die Datenbank aus einem bestehenden MS SQL Server 2012 (mit dem auch die Anwendung arbeitet und die Daten schreiben UND lesen kann). Dort habe ich die Datenbank mittels detach 'abgehängt', dann auf meinen Rechner (MS SQL 2014) kopiert und mit attach eingebunden. Versuche ich nun die Daten auf meinem Rechner zu lesen erhalte ich immer ein byte[] in dem die Daten UTF8 codiert vorliegen (16bit pro 'Byte' - immer das 'richtige' Byte gefolgt von einem Byte mit dem Wert 0x00). Mittels Encoding kann ich diese Daten problemlos verarbeiten und die ursprüngliche Datei wiederherstellen).

Verwende ich nun mein Programm gegen den ursprünglichen SQL Server 2012, erhalte ich das beschriebene Verhalten - also immer 'O_L_E_-O_b_j_e_k_t' wobei der Unterstrich für 0x00 steht. Schaue ich mir die Tabelle mit dem SQL Manager an, kann ich die korrekten Daten lesen.

Meine Vermutung ging nun in die Richtung, dass ich auf dem MS SQL Server noch irgendwas einstellen muss/kann, dass er die tatsächlichen 'Binärdaten' liefert - allerdings finde ich nichts dazu.

@BerndFfm
Den Artikel in Deinem Link kannt ich shcon - auch mit dem Code dort scheitert es immer wieder an der Zeile:

      byte[] bData = (byte[])cmdQuery.ExecuteScalar();

die bereits die falschen 'Rohdaten' liefert.

Ich werde morgen nochmal versuchen mit einer anderen Framework-Version als der derzeit genutzten 4.5 zu arbeiten - vllt. ist es ja auch ein 'Bug' im .NET-Framework.