从数据库中读取SQL Varbinary Blob

时间:2016-09-16 09:16:13

标签: c# sql .net sql-server blob

我正在努力将文件保存到sql blob到varbinary(max)列,并且现在已经保存了一些工作(我相信)。

我无法弄清楚如何读取数据,因为我使用存储过程检索我的数据库值我应该能够像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;
                        }
                    }
                }
            }
        }
    }

当我可以在没有sqlcommand的情况下访问我需要的列时,有更简单的方法吗?

希望我在我的问题中足够清楚,如果没有,那么请问我会详细说明!

更新:

现在的情况是这样 - 我有我的存储过程返回的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();
                }
            }

干杯, ħ

3 个答案:

答案 0 :(得分:16)

你使它变得比它需要的更困难。这是使用MySQL只是因为它很方便 - 提供者都工作几乎相同。需要调整一些东西来处理非常大的数据项(更多的是服务器而不是DB Provider)。

保存图像

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 Provider

  

有没有办法获得完整路径,包括存储在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,除非您不需要显示,否则无需创建临时Image

总而言之,Ceiling Cat让它恢复得很好(图像是1.4 MB,缩放;另一个15.4 MB图像的测试也起作用 - 两者都比我想要存储在数据库中的大一些)。:

enter image description here

根据使用方法的不同,请考虑将图像存档到文件系统的某个位置,只需保存文件名 - 也许添加Id以确保名称是唯一的,并帮助将它们直观地链接到记录。大型数据不仅会使数据库膨胀,而且显然有一些开销涉及到可以避免的字节转换。

如果您希望/需要在关联的应用程序完成之后的某个时刻删除它们(实际上不是问题的一个组成部分),那么在特定目录中使用临时文件,以便删除其中的所有内容(有条件地< sup> 1 )当应用程序结束或启动时:

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

附加Temp文件名和单个文件的实际扩展名。或者,您可以维护List<string> trashCan来存储您创建的每个文件的名称,以便稍后删除。

1 每当您删除它们时,执行允许该文件仍然可以在与该扩展程序相关联的应用中打开。

答案 1 :(得分:7)

使用.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或文件流中使用。

答案 2 :(得分:6)

当数据存储在varbinary(MAX)列中时,您将需要使用SqlCommand来检索数据,除非您使用FileTable,它允许通过UNC路径访问内容,类似于存储在a上的常规文件文件系统,但由SQL Server管理。

如果blob的大小可能很大,那么您当前使用的“块”技术将减少内存需求,但代价是更冗长的代码。对于合理大小的blob大小,您可以在不使用分块方法的情况下立即读取整个列内容。这是否可行取决于blob的大小和客户端可用内存。

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