SQL Server:如何将存储在 varbinary(max) 字段中的文件(pdf、doc、txt...)复制到 CLR 存储过程中的文件?

发布于 2024-08-31 10:21:49 字数 1778 浏览 3 评论 0原文

我问这个问题是 这个问题

使用 bcp 和 xp_cmdshell 的解决方案(这不是我想要的解决方案)已发布 此处

我是 C# 新手(因为我是一名 Delphi 开发人员),无论如何,我能够通过遵循教程创建一个简单的 CLR 存储过程。

我的任务是将文件从客户端文件系统移动到服务器文件系统(可以使用远程IP访问服务器,因此我不能使用共享文件夹作为目标,这就是我需要CLR存储过程的原因)。

所以我计划:

  1. 从 Delphi 将文件存储在临时表的 varbinary(max) 列中
  2. 调用 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 存储过程,这样我就不需要使用临时表?)

如果子问题的答案为“否” ,您能描述一下避免使用临时表的方法吗?那么有没有比我上面描述的更好的方法(=临时表+存储过程)?直接将数据流从客户端应用程序传递到 CLR 存储过程的方法? (我的文件可以是任何大小,但也很大)

I ask this question as a followup of this question.

A solution that uses bcp and xp_cmdshell, that is not my desired solution, has been posted here.

I am new to c# (since I am a Delphi developer) anyway I was able to create a simple CLR stored procedure by following a tutorial.

My task is to move a file from the client file system to the server file system (the server can be accessed using remote IP, so I cannot use a shared folder as destination, this is why I need a CLR stored procedure).

So I plan to:

  1. store from Delphi the file in a varbinary(max) column of a temporary table
  2. call the CLR stored procedure to create a file at the desired path using the data contained in the varbinary(max) field

Imagine I need to move C:\MyFile.pdf to Z:\MyFile.pdf, where C: is a harddrive on local system and Z: is an harddrive on the server. C is in New York, Z is in London and there is no VPN between them, just https connection.

I provide the code below (not working) that someone can modify to make it work? Here I suppose to have a table called MyTable with two fields: ID (int) and DATA (varbinary(max)). Please note it doesn't make a difference if the table is a real temporary table or just a table where I temporarly store the data. I would appreciate if some exception handling code is there (so that I can manage an "impossible to save file" exception).

I would like to be able to write a new file or overwrite the file if already existing.

[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');
   }
}

(one subquestion is: is this approach correct or there is a way to directly pass the data to the CLR stored procedure so I don't need to use a temp table?)

If the subquestion's answer is No, could you describe the approach of avoiding a temp table? So is there a better way then the one I describe above (=temp table + Stored procedure)? A way to directly pass the dataastream from the client application to the CLR stored procedure? (my files can be any size but also very big)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

逆流 2024-09-07 10:21:49

有没有办法直接将数据传递到 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_SETEXTERNAL_ACCESS 的代码>。

在这两种情况下,您都可以按以下方式执行它们:

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

“0x2A20202A”应该为您提供一个包含以下 4 个字符(星号、空格、空格、星号)的文件:

* *

is [there] a way to directly pass the data to the CLR stored procedure so I don't need to use a temp table?

Yes, it is both possible and rather simple to pass a binary file to a SQLCLR stored procedure and have it write the contents to disk, and not require first placing those contents into a table--temporary or real.

[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;
}

Or, since you said that the files are sometimes large, the following should be much easier on memory usage as it makes use of the streaming functionality:

[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;
}

The assembly containing this code will, of course, need to have a PERMISSION_SET of EXTERNAL_ACCESS.

In both cases, you would execute them in the following manner:

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

And "0x2A20202A" should give you a file containing the following 4 characters (asterisk, space, space, asterisk):

* *

剩余の解释 2024-09-07 10:21:49

为什么要把这些文件放入数据库?如果您有 http/https 连接,您可以将文件上传到服务器,写入受保护的目录并创建一个页面来列出这些文件并提供下载链接。如果你想存储一些额外的信息,你可以将其写入数据库。您只需要在服务器端更改文件名(使用唯一的名称)。

Why are you putting these files into database? If you have http/https connection you can upload the file to the server, write into a protected dierctory and create a page to list those files and give a link to download it. If you want to store some extra information you can write it into database. You just need to change the name of file at server side (use a unique name).

中性美 2024-09-07 10:21:49

经过一番研究,我得出结论,这没有意义,最好放弃 2005 的支持并使用 2008 fielstream 功能。我可以有一个条件逻辑来在 2005 年和 2008 年之间进行选择,并仅在 2005 年使用文件流。

After some research I conclude that it makes no sense, it is better to drop the support of 2005 and use 2008 fielstream feature. I can have a conditional logic to choose between 2005 and 2008 and use filestream only for 2005.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文