【问题标题】:File gets corrupted when reading/writting from SQLServer 2005/2008 from C#?从 C# 读取/写入 SQL Server 2005/2008 时文件损坏?
【发布时间】:2011-01-22 19:19:50
【问题描述】:

我编写了两个小方法来保存和加载 .docx(以及后来的其他类型文件)文件到数据库(SERVER 2005/2008,以 VarBinary(MAX) 作为列)。一切看起来都不错,但是当我读回文件时,它已创建,但 Word 抱怨它已损坏,但最终将其打开,其中包含所有内容。代码有什么问题?

    public static void databaseFileRead(string varID, string varPathToNewLocation) {
        const int bufferSize = 100;
        byte[] outByte = new byte[bufferSize];
        using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP))
        using (var sqlQuery = new SqlCommand(@"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = @varID", varConnection)) {
            sqlQuery.Parameters.AddWithValue("@varID", varID);
            using (var sqlQueryResult = sqlQuery.ExecuteReader(CommandBehavior.Default))
                while (sqlQueryResult != null && sqlQueryResult.Read()) {
                    using (FileStream stream = new FileStream(varPathToNewLocation, FileMode.OpenOrCreate, FileAccess.Write)) {
                        using (BinaryWriter writer = new BinaryWriter(stream)) {
                            long startIndex = 0;
                            long retval = sqlQueryResult.GetBytes(0, startIndex, outByte, 0, bufferSize);
                            while (retval == bufferSize) {
                                writer.Write(outByte);
                                writer.Flush();
                                startIndex += bufferSize;
                                retval = sqlQueryResult.GetBytes(0, startIndex, outByte, 0, bufferSize);
                            }
                            writer.Write(outByte, 0, (int) retval - 1);
                            writer.Flush();
                            writer.Close();
                        }
                        stream.Close();
                    }
                }
        }
    }
    public static void databaseFilePut(string varFilePath) {
        FileStream stream = new FileStream(varFilePath, FileMode.Open, FileAccess.Read);
        BinaryReader reader = new BinaryReader(stream);
        byte[] file = reader.ReadBytes((int) stream.Length);
        reader.Close();
        stream.Close();
        using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP))
        using (var sqlWrite = new SqlCommand("INSERT INTO Raporty (RaportPlik) Values(@File)", varConnection)) {
            sqlWrite.Parameters.Add("@File", SqlDbType.Binary, file.Length).Value = file;
            sqlWrite.ExecuteNonQuery();
        }
    }

编辑:

我已按照建议将代码更改为 bufferSize 为 4096,但仍然不行。

原始文件显示:大小为 48,0 KB(字节:49 225),磁盘大小为 52,0 KB(字节:53 248)(Win 7 属性显示了这一点)。从 db 中取出的文件大小为 52,0 KB(字节:53 248),磁盘大小为 52,0 KB(字节:53 248)。

这一切都发生在使用 Win 7 x64 的开发机器上,为了确定,我已经卸载了 Eset Smart Security。

编辑2:

所以我从webpage 添加了另一种“方法”,它似乎可以解决问题。唯一值得注意的区别是缺少使用 BinaryWriter 和 Byte[] blob 的有点奇怪的定义。 是不是很奇怪?

    public static void databaseFileRead(string varID, string varPathToNewLocation) {
        const int bufferSize = 4096;
        byte[] outByte = new byte[bufferSize];
        using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP))
        using (var sqlQuery = new SqlCommand(@"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = @varID", varConnection)) {
            sqlQuery.Parameters.AddWithValue("@varID", varID);
            using (var sqlQueryResult = sqlQuery.ExecuteReader())
                while (sqlQueryResult != null && sqlQueryResult.Read()) {
                    using (FileStream stream = new FileStream(varPathToNewLocation, FileMode.OpenOrCreate, FileAccess.Write))
                    using (BinaryWriter writer = new BinaryWriter(stream)) {
                        long startIndex = 0;
                        long retval = sqlQueryResult.GetBytes(0, startIndex, outByte, 0, bufferSize);
                        while (retval > 0) {
                            writer.Write(outByte);
                            writer.Flush();
                            startIndex += retval;
                            retval = sqlQueryResult.GetBytes(0, startIndex, outByte, 0, bufferSize);
                        }
                    }
                }
        }
        Byte[] blob = null;
        FileStream fs = null;
        const string sConn = Locale.sqlDataConnectionDetailsDZP;
        SqlConnection conn = new SqlConnection(sConn);
        SqlCommand cmd = new SqlCommand("SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = " + varID, conn);
        conn.Open();
        SqlDataReader sdr = cmd.ExecuteReader();
        sdr.Read();
        blob = new Byte[(sdr.GetBytes(0, 0, null, 0, int.MaxValue))];
        sdr.GetBytes(0, 0, blob, 0, blob.Length);
        sdr.Close();
        conn.Close();
        fs = new FileStream("c:\\Builder.docx", FileMode.Create, FileAccess.Write);
        fs.Write(blob, 0, blob.Length);
        fs.Close();
    }

【问题讨论】:

    标签: c# sql-server-2005 sql-server-2008 upload download


    【解决方案1】:

    您的databaseFileRead 方法中有错误。

    考虑一下:您的 bufferSize 为 100 字节(真的非常小 - 我建议至少 4096 字节!)并且您的循环如下所示:

    while (retval == bufferSize) 
    {
       writer.Write(outByte);
       writer.Flush();
       startIndex += bufferSize;
       retval = sqlQueryResult.GetBytes(0, startIndex, outByte, 0, bufferSize);
    }
    

    好的,这很好,直到您的文件中只剩下 73 个字节要处理 - 在这种情况下,最后一次调用

    retval = sqlQueryResult.GetBytes(0, startIndex, outByte, 0, bufferSize);

    将在retval 中返回“73”,因为那是不是 == bufferSize,您将中止。所以有了这个,你总是会跳过最后几个字节.....

    你需要做的是:

    while (retval > 0) 
    {
       writer.Write(outByte);
       writer.Flush();
       startIndex += retval;
       retval = sqlQueryResult.GetBytes(0, startIndex, outByte, 0, bufferSize);
    }
    

    这样,在读取最后 73 个字节时,您将得到 retval=73,然后您将写出最后 73 个字节,然后对 sqlQueryResult 的下一次调用应该返回 retval=0,然后终止您的循环。

    试试看 - 我很确定这是导致此错误的原因。

    马克

    【讨论】:

    • 我已将代码替换为您的代码,但仍然出错。我也将缓冲区大小更改为 4096。我还认为writer.Write(outByte, 0, (int) retval - 1); 正在处理丢失的 73 个字节。代码基于msdn.microsoft.com/en-us/library/87z0hy49%28VS.80%29.aspx
    • @madboy:同样的错误?你有什么额外的信息吗?磁盘上生成的文件大小是 100 的精确倍数。 4096 字节??
    • @madboy:是的,没错——我忽略了最后一个writer.Write 声明。是的,这将处理最后剩余的字节......
    • 我现在又做了一次测试。原始文件显示:48,0 KB (bajtów: 49 225) 作为磁盘大小,52,0 KB (bajtów: 53 248) 作为磁盘大小(Win 7 属性显示了这一点)。从 db 中取出的文件大小为 52,0 KB (bajtów: 53 248),磁盘大小为 52,0 KB (bajtów: 53 248)。请记住,我赢了 7 x64 可能会导致它变坏?
    • 我已经用工作代码更新了主帖(读取功能的第二部分)。似乎使用 BinaryWriter 不是最好的。不知道你为什么:-)
    【解决方案2】:

    代码可能没有任何问题。我会查看您在该服务器上安装了哪些病毒扫描软件并立即将其卸载。我已经看到 Norton 和 McAfee 都破坏了文件,但没有任何迹象表明它们改变了任何东西。

    【讨论】:

    • 服务器上没有防病毒/防火墙。
    【解决方案3】:

    以下代码似乎可以正常工作。跳过BinaryWriter 的使用使其正确写入文件。为什么使用BinaryWriter 会破坏我不知道的文件:-)

        public static void databaseFileRead(string varID, string varPathToNewLocation) {
            using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetailsDZP))
            using (var sqlQuery = new SqlCommand(@"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = @varID", varConnection)) {
                sqlQuery.Parameters.AddWithValue("@varID", varID);
                using (var sqlQueryResult = sqlQuery.ExecuteReader()) {
                    if (sqlQueryResult != null) {
                        sqlQueryResult.Read();
                        byte[] blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
                        sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
                        using (FileStream fs = new FileStream(varPathToNewLocation, FileMode.Create, FileAccess.Write)) {
                            fs.Write(blob, 0, blob.Length);
                        }
                    }
    
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-12
      • 1970-01-01
      相关资源
      最近更新 更多