web-dev-qa-db-fra.com

Lecture d'un blob varbinaire SQL à partir d'une base de données

Je travaille sur l'enregistrement de fichiers dans blob sql dans une colonne varbinary (max), et j'ai le côté sauvegarde des choses qui fonctionne maintenant (je crois).

Ce que je ne peux pas comprendre, c'est comment lire les données, étant donné que je récupère mes valeurs de base de données à l'aide d'une procédure stockée, je devrais pouvoir accéder aux données de la colonne comme ds.Tables [0] .Rows [0] [ "blobData"]; est-il donc nécessaire que j'ai un SQLCommand etc comme je l'ai vu dans des exemples tels que celui ci-dessous:

private void OpenFile(string selectedValue)
{
    String connStr = "...connStr";
    fileName = ddlFiles.GetItemText(ddlFiles.SelectedItem);

    using (SqlConnection conn = new SqlConnection(connStr))
    {
        conn.Open();
        using (SqlCommand cmd = conn.CreateCommand())
        {
            cmd.CommandText = "SELECT BLOBData FROM BLOBTest WHERE testid = " + selectedValue;

            using (SqlDataReader dr = cmd.ExecuteReader())
            {
                while (dr.Read())
                {
                    int size = 1024 * 1024;
                    byte[] buffer = new byte[size];
                    int readBytes = 0;
                    int index = 0;

                    using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
                    {
                        while ((readBytes = (int)dr.GetBytes(0, index, buffer, 0, size)) > 0)
                        {
                            fs.Write(buffer, 0, readBytes);
                            index += readBytes;
                        }
                    }
                }
            }
        }
    }

Existe-t-il un moyen plus simple de le faire lorsque je peux accéder à la colonne dont j'ai besoin sans la commande sql?

J'espère que j'ai été assez clair dans ma question, sinon demandez-le et je développerai!

MISE À JOUR:

La situation est maintenant la suivante: j'ai la valeur de la colonne blobData renvoyée par ma procédure stockée et je peux la transmettre à un flux de mémoire et appeler 'LoadDocument (memStream); cependant, cela se traduit par du charabia au lieu de l'affichage de mon fichier réel.

Ma question est maintenant: existe-t-il un moyen d'obtenir le chemin complet, y compris l'extension de fichier d'un fichier stocké dans un SQL Blob? Je cherche actuellement à utiliser un fichier pour cela dans l'espoir que je pourrai obtenir le chemin complet.

MISE À JOUR 2:

J'ai essayé de créer un fichier temporaire et de le lire en vain (toujours du charabia)

                string fileName = System.IO.Path.GetTempFileName().ToString().Replace(".tmp", fileExt);

            using (MemoryStream myMemoryStream = new MemoryStream(blobData, 0, (int)blobData.Length, false, true))
            {
                using (FileStream myFileStream1 = File.Create(fileName))
                {
                    myMemoryStream.WriteTo(myFileStream1);

                    myMemoryStream.Flush();
                    myMemoryStream.Close();

                    myFileStream1.Flush();
                    myFileStream1.Close();

                    FileInfo fi = new FileInfo(fileName);

                    Process prc = new Process();
                    prc.StartInfo.FileName = fi.FullName;
                    prc.Start();
                }
            }

À la vôtre, H

15
bjjrolls

Vous rendez les choses plus difficiles que nécessaire. Cela utilise MySQL juste parce que c'est pratique - les fournisseurs fonctionnent tous à peu près de la même manière. Certaines choses devront être ajustées pour gérer des éléments de données très volumineux (plus une chose serveur que DB Provider).

Sauvegarder l'image

string sql = "INSERT INTO BlobDemo (filename, fileType, fileData) VALUES (@name, @type, @data)";
byte[] imgBytes;

using (MySqlConnection dbCon = new MySqlConnection(MySQLConnStr))
using (MySqlCommand cmd = new MySqlCommand(sql, dbCon))
{  
    string ext = Path.GetExtension(filename);

    dbCon.Open();
    cmd.Parameters.Add("@name", MySqlDbType.String).Value = "ziggy";
    cmd.Parameters.Add("@data", MySqlDbType.Blob).Value = File.ReadAllBytes(filename);
    cmd.Parameters.Add("@tyoe", MySqlDbType.String).Value = ext;
    int rows = cmd.ExecuteNonQuery();
}

Les données du fichier sont transmises directement au fournisseur DB

existe-t-il un moyen d'obtenir le chemin d'accès complet, y compris l'extension de fichier d'un fichier stocké dans un objet SQL Blob?

Non. Votre code et le code ci-dessus enregistrent les - octets qui composent une image ou n'importe quel fichier.

Lire les données Img

Cela va lire les données, les enregistrer dans un fichier et démarrer l'application associée:

string SQL = "SELECT itemName, itemData, itemtype FROM BlobDemo WHERE Id = @id";

string ext = "";
string tempFile = Path.Combine(@"C:\Temp\Blobs\", 
    Path.GetFileNameWithoutExtension(Path.GetTempFileName())); 

using (MySqlConnection dbCon = new MySqlConnection(MySQLConnStr))
using (MySqlCommand cmd = new MySqlCommand(SQL, dbCon))
{
    cmd.Parameters.Add("@id", MySqlDbType.Int32).Value = 14;
    dbCon.Open();

    using (MySqlDataReader rdr =  cmd.ExecuteReader())
    {
        if (rdr.Read())
        {
            ext = rdr.GetString(2);
            File.WriteAllBytes(tempFile + ext, (byte[])rdr["itemData"]);
        }
    }

    // OS run test
    Process prc = new Process();
    prc.StartInfo.FileName = tempFile + ext;
    prc.Start();
}
  • 1 Le nombre d'octets relus correspond
  • 1 L'application associée lancée très bien avec l'image
  • 1 L'image montrée dans la Picturebox

Dans les deux cas, File.ReadAllBytes() et File.WriteAllBytes() feront la plupart du travail pour vous, quel que soit le type de fichier.

Il n'est pas nécessaire d'extraire les données 1k à la fois. Si le blob était quelque chose comme une image que vous souhaitiez utiliser dans l'application:

using (MySqlDataReader rdr = cmd.ExecuteReader())
{
    if (rdr.Read())
    {
        ext = rdr.GetString(2);
        using (MemoryStream ms = new MemoryStream((byte[])rdr["imgData"]))
        {
            picBox.Image = Image.FromStream(ms);
        }
    }
}

Les octets blob peuvent être fournis au memstream, et même un temp Image n'a pas besoin d'être créé sauf si vous n'avez pas besoin de le montrer.

En tout, Ceiling Cat est revenu très bien (l'image était de 1,4 Mo, zoomée; un autre test avec une image de 15,4 Mo a également fonctionné - les deux sont plus grands que je ne voudrais stocker dans une base de données).

enter image description here

Selon la façon dont cela est utilisé, pensez à archiver les images quelque part sur le système de fichiers et à simplement enregistrer le nom de fichier - peut-être avec le Id ajouté pour vous assurer que les noms sont uniques et aider à les lier visuellement à l'enregistrement. Non seulement de gros blobs de données gonflent la base de données, mais il y a évidemment des frais généraux impliqués dans la conversion vers et depuis les octets qui peuvent être évités.


Si vous souhaitez/devez les supprimer à un moment donné après que l'application associée en a fini avec eux (pas vraiment un composant de la question), utilisez un fichier temporaire dans un répertoire spécifique afin de pouvoir tout supprimer (conditionnellement1) à la fin de l'application ou au démarrage:

private string baseAppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                    "Company Name", "Product Name", "Temp Files");

Ajoutez un nom de fichier temporaire et l'extension réelle pour les fichiers individuels. Vous pouvez également conserver un List<string> trashCan Pour stocker le nom de chaque fichier que vous créez pour être supprimé ultérieurement.

1 Chaque fois que vous les supprimez, autorisez que les fichiers puissent toujours être ouverts dans l'application associée à l'extension.

18

Avec le fournisseur .NET SQL Server, vous pouvez utiliser une classe peu connue mais cool appelée SqlBytes . Il a été conçu spécifiquement pour mapper les champs varbinary, mais il n'y a pas beaucoup d'exemples sur la façon de l'utiliser.

Voici comment vous pouvez enregistrer dans la base de données avec elle (vous pouvez utiliser une procédure stockée ou SQL direct comme je le démontre ici, nous supposons simplement que MyBlobColumn est un varbinary one).

string inputPath = "YourInputFile";
using (var conn = new SqlConnection(YourConnectionString))
{
    conn.Open();
    using (var cmd = conn.CreateCommand())
    {
        // note we define a '@blob' parameter in the SQL text
        cmd.CommandText = "INSERT INTO MyTable (Id, MyBlobColumn) VALUES (1, @blob)";
        using (var inputStream = File.OpenRead(inputPath))
        {
            // open the file and map it to an SqlBytes instance
            // that we use as the parameter value.
            var bytes = new SqlBytes(inputStream);
            cmd.Parameters.AddWithValue("blob", bytes);

            // undercovers, the reader will suck the inputStream out through the SqlBytes parameter
            cmd.ExecuteNonQuery();
        }
    }
}

Pour lire le fichier dans un flux de la base de données, voici comment vous pouvez le faire.

string outputPath = "YourOutputFile";
using (var conn = new SqlConnection(YourConnectionString))
{
    conn.Open();
    using (var cmd = conn.CreateCommand())
    {
        // this is a regular direct SQL command, but you can use a stored procedure as well
        cmd.CommandText = "SELECT MyBlobColumn FROM MyTable WHERE Id = 1";

        // note the usage of SequentialAccess to lower memory consumption (read the docs for more)
        using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
        {
            if (reader.Read())
            {
                // again, we map the result to an SqlBytes instance
                var bytes = reader.GetSqlBytes(0); // column ordinal, here 1st column -> 0

                // I use a file stream, but that could be any stream (asp.net, memory, etc.)
                using (var file = File.OpenWrite(outputPath))
                {
                    bytes.Stream.CopyTo(file);
                }
            }
        }
    }
}

Avec ces techniques, nous n'avons jamais alloué d'octets [] ni d'instances MemoryStream, juste utilisées dans et hors de SQL ou de flux de fichiers.

7
Simon Mourier

Vous devrez utiliser un SqlCommand pour récupérer des données lorsque les données sont stockées dans une colonne varbinary (MAX) sauf si vous utilisez un FileTable , qui permet d'accéder au contenu via le chemin UNC de la même manière que les fichiers ordinaires stockés sur un système de fichiers, mais géré par SQL Server.

Si la taille de l'objet blob peut être importante, la technique de "bloc" que vous utilisez actuellement réduira les besoins en mémoire, mais au détriment d'un code plus détaillé. Pour des tailles de blob de taille raisonnable, vous pouvez lire le contenu de la colonne en une seule fois sans méthode de segmentation. La faisabilité dépend de la taille du blob et de la mémoire disponible du client.

var buffer = (byte[])cmd.ExecuteScalar();
fs.Write(buffer, 0, buffer.Length);
6
Dan Guzman