SQL Server: как скопировать файл (pdf, doc, txt…), хранящийся в поле varbinary (max), в файл в хранимой процедуре CLR?

2

Я задаю этот вопрос как следующий этот вопрос.

Решение, использующее bcp и xp_cmdshell, это не мое желаемое решение, было опубликовано здесь.

Я новичок в С# (поскольку я разработчик Delphi), в любом случае мне удалось создать простую процедуру хранения CLR, выполнив учебное пособие.

Моя задача - переместить файл из файловой системы клиента в файловую систему сервера (к серверу можно получить доступ с помощью удаленного IP-адреса, поэтому я не могу использовать общую папку в качестве адресата, поэтому мне нужна хранимая процедура CLR).

Итак, я планирую:

  • хранить из Delphi файл в столбце varbinary (max) временной таблицы
  • вызов хранимой процедуры CLR для создания файла по желаемому пути с использованием данных, содержащихся в поле varbinary (max)

Представьте, что мне нужно переместить C:\MyFile.pdf на Z:\MyFile.pdf, где C: жесткий диск в локальной системе, а Z: жесткий диск на сервере. C находится в Нью-Йорке, Z находится в Лондоне, и между ними нет VPN, только соединение https.

Я предоставляю код ниже (не работает), который кто-то может изменить, чтобы заставить его работать? Здесь я предполагаю иметь таблицу MyTable с двумя полями: ID (int) и DATA (varbinary (max)). Обратите внимание, что это не имеет значения, если таблица является реальной временной таблицей или просто таблицей, где я временно сохраняю данные. Я был бы признателен, если бы там был какой-то код обработки исключений (так что я могу управлять исключением "невозможно сохранить файл" ).

Я хотел бы иметь возможность написать новый файл или перезаписать файл, если он уже существует.

[Microsoft.SqlServer.Server.SqlProcedure]
public static void VarbinaryToFile(int TableId)
{
   using (SqlConnection connection = new SqlConnection("context connection=true"))
   {
      connection.Open();
      SqlCommand command = new SqlCommand("select data from mytable where ID = @TableId", connection);
      command.Parameters.AddWithValue("@TableId", TableId);
      // This was the sample code I found to run a query
      //SqlContext.Pipe.ExecuteAndSend(command);
      // instead I need something like this (THIS IS META_SYNTAX!!!):
      SqlContext.Pipe.ResultAsStream.SaveToFile('z:\MyFile.pdf');
   }
}

(одно подзапрос: этот подход правильный или есть способ напрямую передать данные в хранимую процедуру CLR, поэтому мне не нужно использовать временную таблицу?)

Если ответ на подзапрос равен "Нет", не могли бы вы описать подход к исключению временной таблицы? Так есть лучший способ, чем тот, который я описываю выше (= temp table + Хранимая процедура)? Способ напрямую передать информационный поток из клиентского приложения в хранимую процедуру CLR? (мои файлы могут быть любого размера, но также очень большие)

  • 0
    Никто не может помочь больше, чем это?
Теги:
sql-server
sqlclr
varbinary

4 ответа

0
Лучший ответ

После некоторых исследований я пришел к выводу, что это не имеет смысла, лучше отказаться от поддержки 2005 года и использовать функцию Fielstream 2008 года. Я могу иметь условную логику для выбора между 2005 и 2008 годами и использовать фильтр только для 2005 года.

  • 0
    Привет всем. Я знаю, что прошло чуть более 5 лет, но я опубликовал ответ, который сработал бы ;-).
  • 0
    Привет, я никогда не упоминал 2005, 2008, и поток файлов является для меня ограничением, поэтому ваш ответ не ясен в моем мнении
Показать ещё 1 комментарий
2

есть [там] способ напрямую передать данные в хранимую процедуру CLR, поэтому мне не нужно использовать временную таблицу?

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

[Microsoft.SqlServer.Server.SqlProcedure]
public static void SaveFileToLocalDisk([SqlFacet(MaxSize = -1)] SqlBytes FileContents,
    SqlString DestinationPath)
{
    if (FileContents.IsNull || DestinationPath.IsNull)
    {
        throw new ArgumentException("Seriously?");
    }

    File.WriteAllBytes(DestinationPath.Value, FileContents.Buffer);

    return;
}

Или, поскольку вы сказали, что файлы иногда большие, следующее должно быть намного проще в использовании памяти, поскольку оно использует функциональность потоковой передачи:

[Microsoft.SqlServer.Server.SqlProcedure]
public static void SaveFileToLocalDiskStreamed(
    [SqlFacet(MaxSize = -1)] SqlBytes FileContents, SqlString DestinationPath)
{
    if (FileContents.IsNull || DestinationPath.IsNull)
    {
        throw new ArgumentException("Seriously?");
    }

    int _ChunkSize = 1024;
    byte[] _Buffer = new byte[_ChunkSize];

    using (FileStream _File = new FileStream(DestinationPath.Value, FileMode.Create))
    {
        long _Position = 0;
        long _BytesRead = 0;

        while (true)
        {
            _BytesRead = FileContents.Read(_Position, _Buffer, 0, _ChunkSize);

            _File.Write(_Buffer, 0, (int)_BytesRead);
            _Position += _ChunkSize;

            if (_BytesRead < _ChunkSize || (_Position >= FileContents.Length))
            {
                break;
            }

        }

        _File.Close();

    }

    return;
}

Сборка, содержащая этот код, конечно, должна иметь PERMISSION_SET of EXTERNAL_ACCESS.

В обоих случаях вы выполните их следующим образом:

EXEC dbo.SaveFileToLocalDiskStreamed 0x2A20202A, N'C:\TEMP\SaveToDiskTest.txt';

И "0x2A20202A" должен предоставить вам файл, содержащий следующие 4 символа (звездочка, пробел, пробел, звездочка):

* *

0

Почему вы помещаете эти файлы в базу данных? Если у вас есть соединение http/https, вы можете загрузить файл на сервер, записать в защищенный dierctory и создать страницу для отображения этих файлов и дать ссылку для ее загрузки. Если вы хотите сохранить дополнительную информацию, вы можете записать ее в базу данных. Вам просто нужно изменить имя файла на стороне сервера (используйте уникальное имя).

0
  • 0
    Однажды я вспомнил одну вещь: возможно ли использовать подход, описанный в статье, также для очень больших файлов? Я имею в виду, используя пример из ссылки, можно было бы сделать что-то вроде: exec ns_lib.dbo.ns_txt_file_write @file_name = 'c: \ temp \ test_file.txt', @ file_contents = 0x4D5A90000300000004000 .... (этот длинный список такое содержимое файла)? Конечно, изменив на varbinary (max) второй параметр. Есть ли ограничение на количество данных, которые могут быть переданы таким способом? Если это правда, это означает, что ответ моего подвопроса - ДА.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню