web-dev-qa-db-ja.com

データベースからのSQL Varbinary Blobの読み取り

私はファイルをsql blobにvarbinary(max)列に保存する作業を行っていますが、現在は作業の保存面があります(私は信じています)。

ストアドプロシージャを使用してDB値を取得しているため、ds.Tables [0] .Rows [0] [などの列データにアクセスできるはずです。 "blobData"];以下のような例で見たようなSQLCommandなどが必要です。

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;
                        }
                    }
                }
            }
        }
    }

Sqlコマンドなしで必要な列にアクセスできる場合、これを行う簡単な方法はありますか?

私は質問で十分に明確であったことを願っています、そうでなければ尋ねて、私は詳しく説明します!

更新:

状況はこれです。ストアドプロシージャによって返されたblobData列の値があり、これをメモリストリームに渡して 'LoadDocument(memStream);を呼び出すことができます。ただし、これにより、実際のファイルが表示されるのではなく、意味不明なテキストになります。

私の質問は、SQL Blobに保存されているファイルのファイル拡張子を含むフルパスを取得する方法があるかどうかです。現在、完全なパスを取得できることを期待して、このためにFiletableを使用することを検討しています。

更新2:

私は一時ファイルを作成して、これを無駄に読み込もうとしました(まだ意味不明です)

                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();
                }
            }

乾杯、H

15
bjjrolls

必要以上に難しくしている。これは便利だからという理由だけでMySQLを使用しています-プロバイダーはすべてほぼ同じように機能します。非常に大きなデータ項目を処理するには、いくつかの調整が必要になります(DBプロバイダーというよりはサーバーのものです)。

画像を保存しています

_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();
}
_

ファイルデータはDBプロバイダーに直接供給されます

sQL Blobに保存されているファイルのファイル拡張子を含むフルパスを取得する方法はありますか?

いいえ。コードと上記のコードは、画像または任意のファイルを構成するbytesを保存しています。

Imgデータを読み戻す

これにより、データが読み戻され、ファイルに保存され、関連付けられたアプリが起動します。

_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 リードバックされたバイト数が一致しました
  • 1 関連付けられたアプリは画像でうまく起動しました
  • 1 ピクチャボックスに表示された画像

どちらの場合も、File.ReadAllBytes()File.WriteAllBytes()は、ファイルの種類に関係なく、ほとんどの作業を行います。

一度に1kのデータをスクープする必要はありません。 Blobがアプリで使用したい画像のようなものであった場合:

_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);
        }
    }
}
_

Blobバイトはmemstreamに渡すことができ、表示する必要がない限りtemp Imageを作成する必要もありません。

全体として、Ceiling Catは元に戻りました(画像は1.4 MB、ズーム、15.4 MBの画像での別のテストも機能しました-どちらも私がDBに保存するよりも大きいです)。

enter image description here

これの使用方法に応じて、ファイルシステム上のどこかに画像をアーカイブし、ファイル名を保存することを検討してください。名前が一意であり、視覚的にレコードにリンクできるようにIdを追加します。データの大きな塊がDBを膨らませるだけでなく、回避できるバイトへの変換やバイトからの変換に伴うオーバーヘッドが明らかにあります。


関連付けられたアプリが実際に処理された後(実際には質問のコンポーネントではない)ある時点でこれらを削除したい場合は、特定のディレクトリで一時ファイルを使用して、その中のすべてを削除できます(条件付きで1)アプリの終了時、または起動時:

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

一時ファイル名と個々のファイルの実際の拡張子を追加します。または、_List<string> trashCan_を保持して、作成した各ファイルの名前を保存し、後で削除することもできます。

1 それらを削除するたびに、doは、拡張子に関連付けられたアプリでファイルを開くことができるようにします。

.NET SQL Serverプロバイダーでは、あまり知られていないがクールな SqlBytes というクラスを使用できます。 varbinaryフィールドをマップするために特別に設計されていますが、使用方法に関する例は多くありません。

これを使用してデータベースに保存する方法を次に示します(ここで示すように、ストアドプロシージャまたはダイレクトSQLを使用できます。MyBlobColumnはvarbinaryであると仮定しています)。

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();
        }
    }
}

ファイルをデータベースからストリームに読み込むには、次のようにします。

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);
                }
            }
        }
    }
}

これらの手法では、byte []もMemoryStreamインスタンスも割り当てられず、SQLまたはファイルストリームで使用されます。

7
Simon Mourier

FileTable を使用する場合を除き、データがvarbinary(MAX)列に格納されている場合、SqlCommandを使用してデータを取得する必要があります。ファイルシステムですが、SQL Serverによって管理されます。

BLOBのサイズが大きくなる可能性がある場合、現在使用している「チャンク」手法はメモリ要件を削減しますが、より冗長なコードを犠牲にします。適度なサイズのblobサイズの場合、チャンク方式を使用せずに列の内容全体を一度に読み取ることができます。これが実現可能かどうかは、BLOBのサイズとクライアントが使用可能なメモリの両方に依存します。

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