Модераторы: Akina
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Загрузка файла в базу данных MSSQL 2005 Express, Варианты и недостатки 
:(
    Опции темы
Hobotanius
  Дата 27.10.2011, 09:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 24.7.2008

Репутация: нет
Всего: нет



Необходимо хранить на сервере в базе данных файлы документов (doc). Решаю эту задачу на NET 4, C#, ADO, MSSQL2005Express. Поискал пути решения, и на данный момент собирался использовать для загрузки файла такой вариант:

Создаю таблицу на сервере:
Код

CREATE TABLE [dbo].[Test] (
    [id] int IDENTITY(1,1) NOT NULL,
    [FileName] nvarchar(150) COLLATE Cyrillic_General_CI_AS,
    [DocumentFile] varbinary(max) NULL);
 

Далее клиентское приложение предлагает пользователю выбрать файл для отправки на сервер. Пользователь выбирает файл, приложение берёт путь к файлу, составляет и отправляет sql-запрос:
Код

insert into [dbo].[test](
            [dbo].[test].[FileName], 
            [dbo].[test].[DocumentFile]) 
select ('MyDocument.doc') as FileName, * 
from openrowset(bulk 'C:\MyDocument.doc', single_blob) as DocumentFile
 

Всё это вполне работает если служба sql-сервера и клиентское приложение выполняются на одной машине. Как только это две разных машины, то служба сервера не находит этот файл, так как на диске цэ той машины на которой выполняется служба нет такого файла. Это легко решаемо. Например, клиентская программа может скопировать указанный пользователем файл в папку с общим доступом и прислать в sql-запросе сетевой путь, либо скопировать файл в папку на сервере и так же прислать локальный путь в запросе.
Как мне кажется, эти варианты имеют недостатки. Например: после копирования файла на сервер отсылается запрос на загрузку этого файла в базу данных, но между окончанием копирования в выполнением запроса, файл по ошибке был изменён незвестным участником. Результат - нарушение целостности данных (загружен искажённый файл). Или такой вариант: файл был скопирован в сетевую папку с общим доступом, потом отправлен запрос на загрузку файла в БД, затем прерывается доступ к сетевому ресурсу, и запрос на загрузку не прокатит и вернёт ошибку. К тому же эти два варианта связаны с дополнительными требованиями для работы программы и дополнительными заботами. 
Поэтому я подумал о таком варианте: клиентская программа загружает в память файл преобразует его в строку вида 0x1298649337038... и выполняет sql-запрос:
Код

insert into [dbo].[test](
       [dbo].[test].[FileName], 
       [dbo].[test].[DocumentFile]) 
values('Имя файла.doc', 0x1298649337038...)

Похоже это может прокатить, но наверняка есть ограничение на длину sql-запроса. Вопрос: какая максимальная длина sql-запроса для MSSQL 2005 Express ?

Даже если длины не хватит можно действовать так: клиентское приложение загрузит в память файл, разобьёт его на части, преобразует части в строки вида 0х12345, создаст в БД временную таблицу, загрузит в неё по частям все куски файла, запустит хранимую процедуру которая склеит части занесёт результат в постоянную таблицу и удалит временную.

Может есть менее трудоёмкие варианты решения?


Это сообщение отредактировал(а) Hobotanius - 27.10.2011, 10:00
PM MAIL   Вверх
Akina
Дата 27.10.2011, 10:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 25
Всего: 454



Эммм... вроде bulk insert нормально работает и с UNC-путями...


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Hobotanius
Дата 27.10.2011, 11:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 24.7.2008

Репутация: нет
Всего: нет



Цитата(Akina @  27.10.2011,  10:35 Найти цитируемый пост)
Эммм... вроде bulk insert нормально работает и с UNC-путями...


А я разве говорил, что  bulk insert не работает с сетевыми путями? Где я такое говорил?! Не понимаю к чему относится ваше высказывание... :( 

PM MAIL   Вверх
Akina
Дата 27.10.2011, 12:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 25
Всего: 454



Цитата(Hobotanius @  27.10.2011,  12:16 Найти цитируемый пост)
Не понимаю к чему относится ваше высказывание... 

К тому, что при расположении файла на удалённом ресурсе его по-любому надо принять на сервер... и я не понимаю, почему, вместо того, чтобы принимать сразу в БД, Вы стремитесь принять его в какое-то промежуточное хранилище, хотя чётко знаете, что это удлинит процесс и породит потенцию дополнительных проблем.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Hobotanius
Дата 27.10.2011, 12:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 24.7.2008

Репутация: нет
Всего: нет



Просто не знаю как загрузить сразу в БД запросом openrowset bulk. 
Потому что запрос
Код

insert into [dbo].[test](
            [dbo].[test].[FileName], 
            [dbo].[test].[DocumentFile]) 
select ('MyDocument.doc') as FileName, * 
from openrowset(bulk 'C:\MyDocument.doc', single_blob) as DocumentFile

будет построен на клиенте а выполняться на серваке. А на серваке такого файла нет. А там где этот файл есть не обязательно есть общий доступ. Поэтому я и думаю о том как обойти это... Пересмотрел варианты в инете, но так и не понял как они работают. Хотя вот щас нашёл пример OLE щас попробую переделать.

PM MAIL   Вверх
Akina
Дата 27.10.2011, 13:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 25
Всего: 454



Цитата(Hobotanius @  27.10.2011,  13:38 Найти цитируемый пост)
А на серваке такого файла нет. 

Ясен пень.

Цитата(Hobotanius @  27.10.2011,  13:38 Найти цитируемый пост)
там где этот файл есть не обязательно есть общий доступ.

То есть получается, что ты должен на клиента возложить обязанность доставить файл на сервер. 
Но в то же время ты пробуешь решать задачу именно средствами сервера - так ничего не получится.

В текст запроса запихивать файл - идея однозначно плохая... а что, если в этом 'MyDocument.doc' окажется рисунок? или, как любят некоторые му ламеры, отсканенный документ на двадцать страниц в 600 dpi?

Загружай файл на сервер по FTP (само собой неанонимно, и оттуда файл должен быть доступен SQL-серверу), и оттуда забирай. А по крону FTP зачищай (скажем раз в 10 минут файлы старше получаса).


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Hobotanius
Дата 27.10.2011, 14:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 24.7.2008

Репутация: нет
Всего: нет



Цитата(Akina @  27.10.2011,  13:23 Найти цитируемый пост)
Загружай файл на сервер по FTP


Нет этот вариант не радует. Этот вариант я уже описал в первом посте - прога просто копирует файл на сервак в папку с общим доступом и отсылает команду загрузки. А по ftp или по другим протоколам разницы принципиальной для меня нет. К тому же вы сами критиковали этот вариант:

Цитата(Akina @  27.10.2011,  12:12 Найти цитируемый пост)
и я не понимаю, почему, вместо того, чтобы принимать сразу в БД, Вы стремитесь принять его в какое-то промежуточное хранилище, хотя чётко знаете, что это удлинит процесс и породит потенцию дополнительных проблем.


Я нашёл тут пример реализации: http://nullpro.info/2011/chteni-i-zapis-dv...yx-fajlov-v-bd/
и попробовал переделать по образцу. Вот что получилось:
Код

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            if (System.IO.File.Exists(textBox1.Text))
            {
                String FileName = System.IO.Path.GetFileName(textBox1.Text);
                byte[] fileByteArray = File.ReadAllBytes(textBox1.Text);

                String str = Properties.Settings.Default.ConnectDatabaseString;
                
                String insert_operation = "insert into [dbo].[test] (" +
                                          "FileName, " +
                                          "DocumentFile) " +
                                          "values (?, ?)";

                int Ret = 0;
                using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.ConnectDatabaseString))
                {
                    conn.Open();

                    SqlCommand cmd = conn.CreateCommand();
                    cmd.CommandText = insert_operation;

                    cmd.Parameters.Add("FileName", System.Data.SqlDbType.NVarChar, 150).Value = FileName;
                    cmd.Parameters.Add("DocumentFile", System.Data.SqlDbType.VarBinary).Value = fileByteArray;

                    try
                    {
                        Ret = cmd.ExecuteNonQuery();
                    }
                    catch (SqlException ё)
                    {
                        MessageBox.Show(ё.Errors[0].Message, "Опрерация вызвала исключение.");
                        MessageBox.Show(insert_operation, "Опрерация вызвала исключение.");

                        return;
                    }

                    conn.Close();
                }

                MessageBox.Show(Ret.ToString(), "");
            }
            else
            { 
                MessageBox.Show("Нет такого файла");
                return;
            }
       }


Однако sql выдаёт ошибку синтаксиса. :(

Неправильный синтаксис около конструкции "?".
Странно как же у автора такое прокатывало?

Это сообщение отредактировал(а) Hobotanius - 27.10.2011, 14:10
PM MAIL   Вверх
Akina
Дата 27.10.2011, 14:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 25
Всего: 454



Цитата(Hobotanius @  27.10.2011,  15:00 Найти цитируемый пост)
К тому же вы сами критиковали этот вариант:

До того, как было указано, что исходное местоположение файла SQL-серверу недоступно.

Цитата(Hobotanius @  27.10.2011,  15:00 Найти цитируемый пост)
А по ftp или по другим протоколам разницы принципиальной для меня нет.

Почему? Это Ваша индивидуальная особенность?

Цитата(Hobotanius @  27.10.2011,  15:00 Найти цитируемый пост)
sql выдаёт ошибку синтаксиса

Попробуйте получить запрос в том виде, в котором он отправляется Sql-серверу...



--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Hobotanius
Дата 27.10.2011, 14:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 24.7.2008

Репутация: нет
Всего: нет



Цитата(Akina @  27.10.2011,  14:15 Найти цитируемый пост)
Почему? Это Ваша индивидуальная особенность?

Да именно так, потому что нет возможности сечас изучать программную передачу по ftp. Пилотный вариант программы должен быть готов к началу декабря - первая неделя декабря. Я и так потратил месяц-полтора на изучение wpf и сейчас вроде как успеваю по срокам, но впритык. Поэтому мне легче и быстрее открыть файл на стороне клиента, а затем создать его копию на севаке и загрузить в БД.

Цитата(Akina @  27.10.2011,  14:15 Найти цитируемый пост)
Попробуйте получить запрос в том виде, в котором он отправляется Sql-серверу...

Да это есть. Вот значение cmd.CommandText в момент возникновения исключения:
Код

insert into [dbo].[test] (FileName, DocumentFile) values (?, ?)


То есть такое ощущение, что строки:
Код

                    cmd.Parameters.Add("FileName", System.Data.SqlDbType.NVarChar, 150).Value = FileName;
                    cmd.Parameters.Add("DocumentFile", System.Data.SqlDbType.VarBinary).Value = fileByteArray;

ни повлияли на запрос. Это странно.

Однако, когда я выполняю этот запрос:
Код

insert into [dbo].[test] (FileName, DocumentFile) values (?, ?)

через SQL Manager, то ошибка которую выдаёт сервак звучит подругому, вот так:
"Отсутствует значение для одного или нескольких требуемых параметров."

Хотя столбцы допускают null.


PM MAIL   Вверх
Akina
Дата 27.10.2011, 15:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 25
Всего: 454



Цитата(Hobotanius @  27.10.2011,  15:48 Найти цитируемый пост)
нет возможности сечас изучать программную передачу по ftp.

Используйте штатный MS Inet Transfer Control 6.0. Изучение которого займёт от силы 10 минут.

Цитата(Hobotanius @  27.10.2011,  15:48 Найти цитируемый пост)
Вот значение cmd.CommandText в момент возникновения исключения

Этот момент - исключения и прочее - лучше обсуждать в разделе "Базы данных под .NET" или около того. Я в оном не силён. 
Там же попробуйте спросить, как получить окончательный, отправляемый серверу, текст запроса, с подставленными в него значениями параметров.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Hobotanius
Дата 27.10.2011, 15:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 24.7.2008

Репутация: нет
Всего: нет



Цитата(Akina @  27.10.2011,  15:20 Найти цитируемый пост)
Используйте штатный MS Inet Transfer Control 6.0. Изучение которого займёт от силы 10 минут.

Сделал запрос в поисковике - MS Inet Transfer Control 6.0 и С# MS Inet Transfer Control 6.0. Но к сожалению на первых пяти страницах информации о том как это использовать под C# не нашёл. Боюсь это займёт не 10 минут.


Цитата(Akina @  27.10.2011,  15:20 Найти цитируемый пост)
Этот момент - исключения и прочее - лучше обсуждать в разделе "Базы данных под .NET" или около того.

Вы имеете ввиду - совсем на другом форуме? Потому как на Винграде такого раздела я что-то не нашёл...

Добавлено через 8 минут
Уже нашёл... Сначала не там искал.
PM MAIL   Вверх
Hobotanius
Дата 27.10.2011, 16:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 24.7.2008

Репутация: нет
Всего: нет



Решение найдено:
Код

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            if (System.IO.File.Exists(textBox1.Text))
            {
                String FileName = System.IO.Path.GetFileName(textBox1.Text);
                byte[] fileByteArray = File.ReadAllBytes(textBox1.Text);

                String str = Properties.Settings.Default.ConnectDatabaseString;

                String insert_operation = "insert into [dbo].[test] (" +
                                          "FileName, " +
                                          "DocumentFile) " +
                                          "values (@Name, @Document)";

                int Ret = 0;
                using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.ConnectDatabaseString))
                {
                    conn.Open();

                    SqlCommand cmd = conn.CreateCommand();
                    cmd.CommandText = insert_operation;

                    cmd.Parameters.Add("@Name", System.Data.SqlDbType.NVarChar, 150).Value = FileName;
                    cmd.Parameters.Add("@Document", System.Data.SqlDbType.VarBinary).Value = fileByteArray;

                    //cmd.Parameters.AddWithValue("FileName", FileName);

                    MessageBox.Show(cmd.CommandText);

                    try
                    {
                        Ret = cmd.ExecuteNonQuery();
                    }
                    catch (SqlException ё)
                    {
                        MessageBox.Show(ё.Errors[0].Message, "Опрерация вызвала исключение.");
                        MessageBox.Show(cmd.CommandText, "Опрерация вызвала исключение.");

                        return;
                    }

                    conn.Close();
                }

                MessageBox.Show(Ret.ToString(), "");
            }
            else
            {
                MessageBox.Show("Нет такого файла");
                return;
            }

        }


Спасибо, Akina, помог форум который вы подсказали.

Это сообщение отредактировал(а) Hobotanius - 27.10.2011, 16:43
PM MAIL   Вверх
Akina
Дата 27.10.2011, 16:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 25
Всего: 454



Цитата(Hobotanius @  27.10.2011,  17:32 Найти цитируемый пост)
Решение найдено

Только проверьте, как оно будет себя мироощущать на файлах в несколько мегабайт размером. И что будет с таблицей БД при обвалах связи.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "MS SQL"
Akina

Akina

Запрещается!

Публиковать ссылки и обсуждать взлом чего бы то ни было.

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы составления неспецифических запросов рассматриваются здесь
  • Используйте теги [code=sql][/code] для подсветки кода. Используйтe чекбокс "транслит" (возле кнопок кодов) если у Вас нет русских шрифтов.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Zloxa, Akina.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | MS SQL Server | Следующая тема »


 




[ Время генерации скрипта: 0.0752 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.