从C#读取/写入SQL Server 2005/2008时文件被破坏?

时间:2010-03-02 16:11:48

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

我写了两个小方法来保存并将.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();
        }
    }

编辑:

我根据建议更改了代码,缓冲区大小为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以确定。

EDIT2:

所以我从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();
    }

3 个答案:

答案 0 :(得分:4)

您的databaseFileRead方法出错。

考虑一下:你有一个100字节的bufferSize(真的很小 - 我建议最少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然后终止你的循环。

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

马克

答案 1 :(得分:1)

代码可能没什么问题。我会查看您在该服务器上使用的病毒扫描软件,并立即将其卸载。我看过诺顿和迈克菲这两个文件都没有任何迹象表明他们改变了什么。

答案 2 :(得分:0)

以下代码似乎没有错误。跳过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);
                    }
                }

            }
        }
    }