【问题标题】:Certain Files getting corrupted by SQL Server FileStream某些文件被 SQL Server FileStream 损坏
【发布时间】:2010-07-06 22:07:45
【问题描述】:

我正在使用 FILESTREAM 将文件保存到 SQL Server 2008 (Express) 数据库,我遇到的问题是某些文件似乎在此过程中损坏了。

例如,如果我以一种较新的格式(docx 或 xslx)保存 word 或 excel 文档,那么当我尝试打开文件时,我会收到一条错误消息,指出数据已损坏,我想要 word/ excel 尝试恢复它,如果我单击是,办公室能够“恢复”数据并以兼容模式打开文件。

但是,如果我先压缩文件,然后在提取内容后,我可以毫无问题地打开文件。奇怪的是,如果我将 mp3 文件保存到数据库,那么我遇到了相反的问题,我可以打开文件没问题,但如果我保存了 mp3 的压缩版本,我什至无法提取该 zip 的内容。当我尝试保存 pdf 或 power-point 文件时,我遇到了类似的问题(我只能先压缩后才能阅读 pdf,而我根本无法阅读 ppt)。

更新:这是我用来写入数据库和读取的代码

写入数据库:

SQL = "SELECT Attachment.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Activity " +
               "WHERE RowID = CAST(@RowID as uniqueidentifier)";
           transaction = connection.BeginTransaction();

           command.Transaction = transaction;
           command.CommandText = SQL;
           command.Parameters.Clear();
           command.Parameters.Add(rowIDParam);

           SqlDataReader readerFS = null;
           readerFS= command.ExecuteReader();

   string path = (string)readerFS[0].ToString();
   byte[] context = (byte[])readerFS[1];
   int length = context.Length;

   SqlFileStream targetStream = new SqlFileStream(path, context, FileAccess.Write);

         int blockSize = 1024 * 512; //half a megabyte
            byte[] buffer = new byte[blockSize];
            int bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
            while (bytesRead > 0)
            {
                targetStream.Write(buffer, 0, bytesRead);
                bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
            }

            targetStream.Close();
            sourceStream.Close();
            readerFS.Close();
            transaction.Commit();

阅读:

        SqlConnection connection = null;
        SqlTransaction transaction = null;

        try
        {
            connection = getConnection();
            connection.Open();
            transaction = connection.BeginTransaction();

            SQL = "SELECT Attachment.PathName(), + GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Activity"
          +  " WHERE ActivityID = @ActivityID";


            SqlCommand command = new SqlCommand(SQL, connection);
            command.Transaction = transaction;

            command.Parameters.Add(new SqlParameter("ActivityID", activity.ActivityID));

            SqlDataReader reader = command.ExecuteReader();

            string path = (string)reader[0];
            byte[] context = (byte[])reader[1];
            int length = context.Length;
            reader.Close();

            SqlFileStream sourceStream = new SqlFileStream(path, context, FileAccess.Read);

            int blockSize = 1024 * 512; //half a megabyte
            byte[] buffer = new byte[blockSize];
           List<byte> attachmentBytes = new List<byte>();

            int bytesRead = sourceStream.Read(buffer, 0, buffer.Length);

            while (bytesRead > 0)
            {
                bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
                foreach (byte b in buffer)
                {
                    attachmentBytes.Add(b);
                }

            }

            FileStream outputStream = File.Create(outputPath);

            foreach (byte b in attachmentBytes)
            {
                 byte[] barr = new byte[1];

                 barr[0] = b;

                 outputStream.Write(barr, 0, 1);
            }

            outputStream.Close();
            sourceStream.Close();
            command.Transaction.Commit();

【问题讨论】:

  • 可能是您的客户端处理插入时发生损坏?例如。过早截断,或使用传递给 SQL 的 varchar() 与 nvarchar() 参数类型,或 char 与二进制?您是否愿意发布用于将内容上传到文件流中的代码以及用于访问文件流的代码?
  • 感谢您的回复,我用我的代码编辑了我的问题。我不确定您使用错误类型参数是什么意思,sql-server 中的字段类型为 Varbinary(max) FILESTREAM)。

标签: c# sql-server-2008 filestream


【解决方案1】:

您读取的代码不正确:

  while (bytesRead > 0)
        {
            bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
            foreach (byte b in buffer)
            {
                attachmentBytes.Add(b);
            }

        }

如果 bytesRead 小于 buffer.Length,您仍将整个缓冲区添加到 attachementBytes。因此,您总是会通过在最后一个缓冲区 post bytesRead 的末尾添加任何垃圾来破坏返回的文档。

除此之外,请允许我有一个真正的 WTF 时刻。将流读取为List&lt;byte&gt; ??来吧!首先,我看不出您需要读取中间内存存储开始的原因。您可以简单地逐个缓冲区读取缓冲区并将每个缓冲区直接写入 outputStream。其次,如果您必须使用中间内存存储,请使用MemoryStream,而不是List&lt;byte&gt;

【讨论】:

  • 谢谢,成功了。我改变了它,以便我直接从输入流写入输出流。
  • 暂时忽略它在读取过程中丢弃第一个 buffer.length 字节 (bytesRead = ..., while (bytesRead > 0), bytesRead = ...)
  • 有趣的是,我最近正在阅读 Jon Skeet's C# in Depth 并且他提到(第 86 页)List&lt;byte&gt; 在许多方面如何使用泛型实际上就像 MemoryStream。我并不是说在这种情况下这是正确的选择,我在这里唯一可以真正为我辩护的是,当我写它时,我的经验要少得多,如果我没记错的话,我是基于我的原始代码的一部分关闭我在网上阅读的使用列表的 sn-p。
  • @Jack 不错的回归 :)
  • 谢谢,我总是对这个问题(以及那个方面)有点尴尬,所以它有点卡在我的脑海里......
【解决方案2】:

几个月前我收到了exact problem,发现我在从 FILESTREAM 读取文件时在文件末尾添加了一个额外的字节。

【讨论】:

  • 我阅读了您的帖子,发现它提供了丰富的信息,因为我想知道为什么我只对特定文件格式有问题(尤其是为什么旧的办公格式似乎没有损坏) .
  • 有同样的问题,但博客文章不再有效。您能否在此处发生的情况以及如何修复它中添加更多详细信息?
猜你喜欢
  • 2011-01-12
  • 1970-01-01
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 2019-11-03
  • 1970-01-01
相关资源
最近更新 更多