Я задаю этот вопрос как следующий этот вопрос.
Решение, использующее bcp и xp_cmdshell, это не мое желаемое решение, было опубликовано здесь.
Я новичок в С# (поскольку я разработчик Delphi), в любом случае мне удалось создать простую процедуру хранения CLR, выполнив учебное пособие.
Моя задача - переместить файл из файловой системы клиента в файловую систему сервера (к серверу можно получить доступ с помощью удаленного IP-адреса, поэтому я не могу использовать общую папку в качестве адресата, поэтому мне нужна хранимая процедура CLR).
Итак, я планирую:
Представьте, что мне нужно переместить 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? (мои файлы могут быть любого размера, но также очень большие)
После некоторых исследований я пришел к выводу, что это не имеет смысла, лучше отказаться от поддержки 2005 года и использовать функцию Fielstream 2008 года. Я могу иметь условную логику для выбора между 2005 и 2008 годами и использовать фильтр только для 2005 года.
есть [там] способ напрямую передать данные в хранимую процедуру 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 символа (звездочка, пробел, пробел, звездочка):
* *
Почему вы помещаете эти файлы в базу данных? Если у вас есть соединение http/https, вы можете загрузить файл на сервер, записать в защищенный dierctory и создать страницу для отображения этих файлов и дать ссылку для ее загрузки. Если вы хотите сохранить дополнительную информацию, вы можете записать ее в базу данных. Вам просто нужно изменить имя файла на стороне сервера (используйте уникальное имя).