【问题标题】:convert varbinary to xml C#将 varbinary 转换为 xml C#
【发布时间】:2017-04-06 08:33:37
【问题描述】:

我有一个将文件保存到 sqlserver 数据库 (2014) 的 C# winforms 应用程序 到 varbinary(MAX) 字段

保存功能

 byte[] Bytefile;
        using (SqlConnection conn = new SqlConnection(DataHelper.GetConnection()))
        {
            conn.Open();
            DataTable dt = new DataTable();
            SqlCommand comm = new SqlCommand("Delete  T_Articale_Files where Artricle_id=" + ID, conn);

            comm.ExecuteNonQuery();
            foreach (string file in Directory.GetFiles(varFilePath))
            {

                using (var stream = new FileStream(Path.Combine(varFilePath, file), FileMode.Open, FileAccess.Read))
                {
                    using (var reader = new BinaryReader(stream))
                    {
                        Bytefile = reader.ReadBytes((int)stream.Length);
                    }
                }


                using (var sqlWrite = new SqlCommand("INSERT INTO T_Articale_Files (Artricle_id,FileName,FileData) Values(@ID,@FileName,@File)", conn))
                {
                    sqlWrite.Parameters.Add("@ID", SqlDbType.Int, 10).Value = ID;
                    sqlWrite.Parameters.Add("@FileName", SqlDbType.NVarChar, 50).Value = Path.GetFileName(file);
                    sqlWrite.Parameters.Add("@File", SqlDbType.VarBinary, file.Length).Value = Bytefile;
                    sqlWrite.ExecuteNonQuery();
                }
            }
        }

检索函数

 using (SqlConnection conn = new SqlConnection(DataHelper.GetConnection()))
        //   using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
        {
            conn.Open();
            DataTable dt = new DataTable();
            SqlCommand comm = new SqlCommand("SELECT id,FileName FROM T_Articale_Files WHERE Artricle_id = @varID", conn);
            comm.Parameters.AddWithValue("@varID", varID);
            dt.Load(comm.ExecuteReader());
            foreach (DataRow item in dt.Rows)
            {
                using (var sqlQuery = new SqlCommand(@"SELECT FileData FROM T_Articale_Files WHERE id = @ID", conn))
                {
                    sqlQuery.Parameters.AddWithValue("@ID", item["id"]);

                    using (var sqlQueryResult = sqlQuery.ExecuteReader())
                        while (sqlQueryResult.Read())
                        {

                            var blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
                            sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
                            using (var fs = new FileStream(Path.Combine(varPathToNewLocation, item["FileName"].ToString()), FileMode.Create, FileAccess.Write))
                                fs.Write(blob, 0, blob.Length);
                        }
                }
            }

        }

工作正常 现在我已经要求将数据库转换为 XML 用于没有连接到服务器的 PC

转换为 XML 的函数

 var xmlFileData = "";
        DataSet ds = new DataSet();
        var tables = new[] { "V_Articale", "T_Articale", "T_City", "T_Classification", "T_Country", "T_Locations", "T_milishia", "T_Search", "T_statistics", "T_TerrorGroups", "T_Tribes", "T_Users", "T_Articale_Files" };
        foreach (var table in tables)
        {

            var query = "SELECT * FROM " + table;
            SqlConnection conn = GetConnection();
            SqlCommand cmd = new SqlCommand(query, conn);
            conn.Open();
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataTable dt = new DataTable(table);
            da.Fill(dt);
            conn.Close();
            conn.Dispose();
            ds.Tables.Add(dt);
            if(table== "T_Articale_Files")
            {
                foreach (DataRow item in dt.Rows)
                {
                    Byte[] file = GetBytes(item["FileData"].ToString());
                }
            }

        }
        xmlFileData = ds.GetXml();

除了二进制文件外,它工作正常

时将其转换为文本

输出 XML

<T_Articale_Files>
<id>6</id>
<Artricle_id>1013</Artricle_id>
<FileName>falcon banner.jpg</FileName>
<FileData>/9j/4AAQSkZJRgABAgEASABIAAD/4QleRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUA</FileData>

当尝试将其转换回图像时,它给了我 1kb 的文件,其中的字符串不是实际图像

二进制字段是否有特殊转换 请指教

谢谢

感谢@grek40 解决了编辑问题 解决方案转换为 XML

  var xmlstream = new StringWriter();
        ds.WriteXml(xmlstream, XmlWriteMode.WriteSchema);
        string xmlWithSchema = xmlstream.ToString();

其中 ds 是一个数据集

转回文件

  private  void databaseFileRead(int varID, string varPathToNewLocation)
    {
         DataSet ds = new DataSet();
        ds.ReadXml(XMLpath);
        DataTable dt = new DataTable();
        dt = ds.Tables["T_Articale_Files"];
        DataView dv = new DataView(dt);
        dv.RowFilter = "Artricle_id=" + varID;

        if (dv.Count > 0)
        {
            foreach (DataRowView item in dv)
            {
                byte[] stringArray = (byte[])(item["FileData"]);
                File.WriteAllBytes(Path.Combine(Filepath, item["FileName"].ToString()), stringArray  ); // save image to disk


            }

        }

【问题讨论】:

  • 如果你不 .ToString() 你的 FileData 怎么办?字节[] 文件 = GetBytes(item["FileData"]);而不是 Byte[] file = GetBytes(item["FileData"].ToString());
  • 对于二进制数据,永远不要使用 ToString(),它总是会搞砸数据。最好将 Encoding.UTF8 与 GetBytes() 或 GetString() 方法一起使用;字符串/char 是两个字节,具有表示 char 是一个字节还是两个字节的受保护属性。当您使用 ToString() 方法时,它可能会在确定一个字符是一个字符还是两个字符时出错。使用 UTF8 将强制每个字符为一个字节。
  • 如果你在谈论 if(table== "T_Articale_Files") { foreach (DataRow item in dt.Rows) { Byte[] file = GetBytes(item["FileData"].ToString() );我评论了该代码并使用了没有它的方法
  • 如果你问 GetBytes 是什么 static byte[] GetBytes(string str) { byte[] bytes = new byte[str.Length * sizeof(char)]; System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);返回字节; }
  • @Maher Khalil 不,它实际上在您的“转换为 XML”功能中。当您将 varbinary(MAX) 转换为 Byte[]

标签: c# sql-server xml


【解决方案1】:

为了对二进制数据进行可逆的字符串编码,您可以使用 Base64 编码

public byte[] StrToByteArray(string str)
{
    return Convert.FromBase64String(str);
}

public string ByteArrToString(byte[] byteArr)
{
    return Convert.ToBase64String(byteArr);
}

将字节转换为存储在 xml 中的字符串,并在使用时从字符串中恢复字节。

最初,数据被正确写入为 xml。问题很可能与检索功能有关。由于 xml 不包含架构信息,除非另有说明,否则它将把 &lt;FileData&gt; 视为文本。

为了允许正确的重新读取,您需要在读取时使用预定义的架构,或者您必须使用表编写架构:

dataSet.WriteXml(filenameOrStream, XmlWriteMode.WriteSchema)
// later read the xml and it will respect the schema information
dataSet.ReadXml(filenameOrStream);

不同方面的小样本:

var sourceDataSet = new DataSet();
var sourceTable = new DataTable("TableWithBinary");

sourceDataSet.Tables.Add(sourceTable);

sourceTable.Columns.Add("Id");
sourceTable.Columns.Add("File", typeof(byte[]));

sourceTable.Rows.Add(1, new byte[] { 1, 0, 2 });
sourceTable.Rows.Add(2, new byte[] { 1, 3, 2 });

// write option 1
string schema = sourceDataSet.GetXmlSchema();
string getxml = sourceDataSet.GetXml();

// write option 2
var writexmlstream = new StringWriter();
sourceDataSet.WriteXml(writexmlstream, XmlWriteMode.WriteSchema);
string writexmlWithSchema = writexmlstream.ToString();

// read wrong (missing schema)
var targetCorrupted = new DataSet();
targetCorrupted.ReadXml(new StringReader(getxml));

// read correct with schema in every xml file
var targetFromXmlWithSchema = new DataSet();
targetFromXmlWithSchema.ReadXml(new StringReader(writexmlWithSchema));

// read correct with separate schema definition and data
var targetFromXml = new DataSet();
targetFromXml.ReadXmlSchema(new StringReader(schema));
targetFromXml.ReadXml(new StringReader(getxml));

【讨论】:

  • @MaherKhalil 您显示了输出 XML,其中 &lt;FileData&gt; 部分已损坏...只需在生成 XML 时使用 ByteArrToString&lt;FileData&gt; 的二进制数据转换为字符串。跨度>
  • 是使用 Function to Convert to XML (Select * from Table) 生成的,现在问题是我对带有二进制表的表有什么特殊处理
  • @MaherKhalil 其实,如果你什么都不做,真的有问题吗?请检查 FileData 列的 DataType。如果是byte[],那么 ds.GetXml() 应该自动具有合适的数据表示。也许您的问题更多在于读取数据而不是写入数据...
  • 1- 数据正确保存到列,因为我可以将列转换回图像,2- 转换为 xml 运行并将二进制列转换为上述 xml 表示形式,3- 我使用检索函数以将图像作为文件返回因此,应该修改将字节保存到 XML 或检索函数的问题
  • @MaherKhalil 我认为您有点误解了 stackoverflow 的概念。我的回答显示了多种读取/写入 xml 为stringTextReader/TextWriter 的方法,并且还有接受Stream 或文件名的已用函数的重载。老实说,我不在乎您选择哪种方式与您的文件进行交互,而且我不会编写您的特定代码 - 这就是您和其他人的报酬,对吧?
【解决方案2】:

二进制 XML的标准格式是base64。我不认为,上面有什么问题......

您必须记住,在 XML 中禁止使用多个字符。您不能只在两个 XML 标记之间填充二进制数据。如果你的二进制文件中偶然有&lt;(或其他禁止字符)的数字代码,它就会中断。

您显示的字符串看起来很像 base64 代码。

试试这个

DECLARE @string VARCHAR(100)='Hello World with forbidden characters (< & >)';
DECLARE @binary VARBINARY(MAX) = CAST(@string AS VARBINARY(MAX));
DECLARE @xml XML=(SELECT @string AS string, @binary AS bin FOR XML PATH('test'));

SELECT @xml;

--结果(带有编码实体和二进制隐式转换为base64)

<test>
  <string>Hello World with forbidden characters (&lt; &amp; &gt;)</string>
  <bin>SGVsbG8gV29ybGQgd2l0aCBmb3JiaWRkZW4gY2hhcmFjdGVycyAoPCAmID4p</bin>
</test>

--现在我们从 XML 中读取(实体被重新编码,二进制表示为 HEX 字符串,这可以重新转换为以前的 VARCHAR(MAX)

SELECT @xml.value('(/test/string)[1]','nvarchar(max)') AS TheStringAsIs
      ,@xml.value('(/test/bin)[1]','varbinary(max)') AS TheStringAsBinary_HEX
      ,CAST(@xml.value('(/test/bin)[1]','varbinary(max)') AS VARCHAR(100)) AS ReConverted

结果

The string *as-is*:  Hello World with forbidden characters (< & >)  
binary data HEX:     0x48656C6C6F20576F726C64207769746820666F7262696464656E206368617261637465727320283C2026203E29   
ReConverted:         Hello World with forbidden characters (< & >) 

【讨论】:

  • 这是 sql 函数而不是 C# 其次是一个文件,我不知道转换后的章程是什么
  • @MaherKhalil 这没关系......原理是一样的。二进制可以存储所有内容。但必须对其进行编码以适应 xml
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-06
  • 1970-01-01
  • 1970-01-01
  • 2011-06-24
相关资源
最近更新 更多