Recupero di valori BLOB da un database

Il funzionamento predefinito di DataReader consiste nel caricare i dati in arrivo come una riga non appena una riga intera di dati è disponibile. È tuttavia necessario gestire gli oggetti binari di grandi dimensioni (BLOB, Binary Large Object) in modo diverso, poiché è possibile che contengano gigabyte di dati che non possono risiedere in una sola riga. Il metodo Command.ExecuteReader dispone di un overload che accetta un argomento CommandBehavior per modificare il funzionamento predefinito dell'oggetto DataReader. È possibile passare CommandBehavior.SequentialAccess al metodo ExecuteReader per modificare il funzionamento predefinito di DataReader in modo che, invece di caricare righe di dati, carichi i dati in modo sequenziale non appena li riceve. Si consiglia di utilizzare questa procedura per caricare BLOB o altre strutture di dati di grandi dimensioni. Si noti che questo funzionamento può variare a seconda dell'origine dati. La restituzione di un BLOB da Microsoft Access comporta, ad esempio, il caricamento in memoria dell'intero BLOB, anziché il caricamento sequenziale in corso di ricezione.

Quando si imposta DataReader per utilizzare SequentialAccess, è importante prestare attenzione alla sequenza con cui si accede ai campi restituiti. Il funzionamento predefinito di DataReader, in base al quale una riga intera viene caricata non appena è disponibile, consente di accedere ai campi restituiti in qualsiasi ordine fino a quando non viene letta la riga successiva. Quando si utilizza SequentialAccess, invece, è necessario accedere ai diversi campi restituiti da DataReader in ordine. Ad esempio, se la query restituisce tre colonne, di cui la terza è un BLOB, è necessario restituire i valori del primo e del secondo campo prima di accedere ai dati BLOB nel terzo campo. Se si accede al terzo campo prima del primo o del secondo, i valori del primo o del secondo campo non saranno più disponibili. Questa situazione si verifica perché SequentialAccess ha modificato DataReader in modo da restituire i dati in sequenza e dopo che sono stati letti da DataReader, i dati non sono più disponibili.

Quando si accede ai dati contenuti nel campo BLOB, utilizzare le funzioni di accesso tipizzate GetBytes o GetChars del DataReader, che riempiono una matrice di dati. Per i dati di tipo carattere, è anche possibile utilizzare GetString. È tuttavia probabile che per contenere l'uso delle risorse di sistema si preferisca non caricare un intero valore BLOB in un'unica variabile di stringa. È possibile specificare una determinata dimensione del buffer da restituire e una posizione iniziale per il primo byte o carattere da leggere dai dati restituiti. GetBytes e GetChars restituiscono un valore long che rappresenta il numero di byte o caratteri restituiti. Se si passa una matrice null a GetBytes o GetChars, il valore long restituito corrisponderà al numero totale di byte o caratteri del BLOB. Se lo si desidera, è possibile specificare un indice nella matrice come posizione iniziale per i dati che vengono letti.

Nell'esempio che segue viene restituito l'ID e il logo dell'editore dal database di esempio pubs in Microsoft SQL Server. L'ID dell'editore (pub_id) è un campo di testo e il logo è un'immagine, quindi un BLOB. Poiché il campo logo è una immagine bitmap, nell'esempio i dati binari vengono restituiti tramite GetBytes. Si noti che all'ID dell'editore nella riga corrente di dati si accede prima del logo, in quanto i campi devono essere letti in modo sequenziale.

Dim pubsConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=pubs;")
Dim logoCMD As SqlCommand = New SqlCommand("SELECT pub_id, logo FROM pub_info", pubsConn)

Dim fs As FileStream                 ' Writes the BLOB to a file (*.bmp).
Dim bw As BinaryWriter               ' Streams the binary data to the FileStream object.

Dim bufferSize As Integer = 100      ' The size of the BLOB buffer.
Dim outbyte(bufferSize - 1) As Byte  ' The BLOB byte() buffer to be filled by GetBytes.
Dim retval As Long                   ' The bytes returned from GetBytes.
Dim startIndex As Long = 0           ' The starting position in the BLOB output.

Dim pub_id As String = ""            ' The publisher id to use in the file name.

' Open the connection and read data into the DataReader.
pubsConn.Open()
Dim myReader As SqlDataReader = logoCMD.ExecuteReader(CommandBehavior.SequentialAccess)

Do While myReader.Read()
  ' Get the publisher id, which must occur before getting the logo.
  pub_id = myReader.GetString(0)

  ' Create a file to hold the output.
  fs = New FileStream("logo" & pub_id & ".bmp", FileMode.OpenOrCreate, FileAccess.Write)
  bw = New BinaryWriter(fs)

  ' Reset the starting byte for a new BLOB.
  startIndex = 0

  ' Read bytes into outbyte() and retain the number of bytes returned.
  retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize)

  ' Continue reading and writing while there are bytes beyond the size of the buffer.
  Do While retval = bufferSize
    bw.Write(outbyte)
    bw.Flush()

    ' Reposition the start index to the end of the last buffer and fill the buffer.
    startIndex += bufferSize
    retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize)
  Loop

  ' Write the remaining buffer.
  bw.Write(outbyte, 0 , retval - 1)
  bw.Flush()

  ' Close the output file.
  bw.Close()
  fs.Close()
Loop

' Close the reader and the connection.
myReader.Close()
pubsConn.Close()
[C#]
SqlConnection pubsConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=pubs;");
SqlCommand logoCMD = new SqlCommand("SELECT pub_id, logo FROM pub_info", pubsConn);

FileStream fs;                          // Writes the BLOB to a file (*.bmp).
BinaryWriter bw;                        // Streams the BLOB to the FileStream object.

int bufferSize = 100;                   // Size of the BLOB buffer.
byte[] outbyte = new byte[bufferSize];  // The BLOB byte[] buffer to be filled by GetBytes.
long retval;                            // The bytes returned from GetBytes.
long startIndex = 0;                    // The starting position in the BLOB output.

string pub_id = "";                     // The publisher id to use in the file name.

// Open the connection and read data into the DataReader.
pubsConn.Open();
SqlDataReader myReader = logoCMD.ExecuteReader(CommandBehavior.SequentialAccess);

while (myReader.Read())
{
  // Get the publisher id, which must occur before getting the logo.
  pub_id = myReader.GetString(0);  

  // Create a file to hold the output.
  fs = new FileStream("logo" + pub_id + ".bmp", FileMode.OpenOrCreate, FileAccess.Write);
  bw = new BinaryWriter(fs);

  // Reset the starting byte for the new BLOB.
  startIndex = 0;

  // Read the bytes into outbyte[] and retain the number of bytes returned.
  retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize);

  // Continue reading and writing while there are bytes beyond the size of the buffer.
  while (retval == bufferSize)
  {
    bw.Write(outbyte);
    bw.Flush();

    // Reposition the start index to the end of the last buffer and fill the buffer.
    startIndex += bufferSize;
    retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize);
  }

  // Write the remaining buffer.
  bw.Write(outbyte, 0, (int)retval - 1);
  bw.Flush();

  // Close the output file.
  bw.Close();
  fs.Close();
}

// Close the reader and the connection.
myReader.Close();
pubsConn.Close();

Vedere anche

Utilizzo di provider di dati .NET Framework per accedere ai dati | Scrittura di valori BLOB in un database | Classe OleDbDataReader | Classe OleDbCommand | Classe OdbcDataReader | Classe OdbcCommand | Classe SqlDataReader | Classe SqlCommand | Enumerazione CommandBehavior