Файл поврежден при чтении / записи из SQLServer 2005/2008 из C #? - PullRequest
1 голос
/ 02 марта 2010

Я написал два небольших метода для сохранения и загрузки файлов .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 КБ (байт: 49 225) как размер и 52,0 КБ (байт: 53 248) как размер на диске (свойства Win 7 показывают это). Размер файла, извлеченного из базы данных, составляет 52,0 КБ (байт: 53 248), а размер диска 52,0 КБ (байт: 53 248).

Все это происходит на компьютере разработчика с Win 7 x64, я просто удалил Eset Smart Security, чтобы быть уверенным.

Edit2:

Итак, я добавил еще один «способ» сделать это с веб-страницы , и это, похоже, помогает. Единственным заметным отличием является отсутствие использования BinaryWriter и немного странное определение для объекта Blob Byte []. Странно, не правда ли?

    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 ]

4 голосов
/ 02 марта 2010

У вас ошибка в вашем databaseFileRead методе.

Учтите это: у вас есть размер буфера 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);

вернет "73" в retval, и поскольку это , а не == 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 голос
/ 02 марта 2010

Вероятно, нет ничего плохого в коде. Я бы посмотрел, какое программное обеспечение для антивирусной проверки установлено на этом сервере, и быстро удалил его. Я видел, как Norton и McAfee закрывали файлы без указания, что они что-то изменили.

0 голосов
/ 03 марта 2010

Следующий код работает без ошибок. Пропуск 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);
                    }
                }

            }
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...