2

この質問のフォローアップとしてこの質問をします。

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接続だけです。

誰かがそれを機能させるために変更できる(機能していない)以下のコードを提供しますか?ここでは、ID(int)とDATA(varbinary(max))の2つのフィールドを持つMyTableというテーブルがあると仮定します。テーブルが実際の一時テーブルであるか、データを一時的に保存するテーブルであるかは、違いがないことに注意してください。いくつかの例外処理コードがあれば幸いです(「ファイルを保存できません」という例外を管理できるようにするため)。

新しいファイルを書き込んだり、既存のファイルを上書きしたりできるようにしたいと思います。

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

(1つのサブ質問は次のとおりです。このアプローチは正しいですか、それともデータをCLRストアドプロシージャに直接渡す方法があるので、一時テーブルを使用する必要はありませんか?)

サブ質問の答えが「いいえ」の場合、一時テーブルを回避する方法を説明してください。それで、私が上で説明したもの(=一時テーブル+ストアドプロシージャ)よりも良い方法はありますか?クライアントアプリケーションからCLRストアドプロシージャにデータストリームを直接渡す方法はありますか?(私のファイルは任意のサイズにすることができますが、非常に大きくすることもできます)

4

4 に答える 4

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ですEXTERNAL_ACCESS

どちらの場合も、次の方法で実行します。

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

また、「0x2A20202A」は、次の4文字(アスタリスク、スペース、スペース、アスタリスク)を含むファイルを提供するはずです。

**

于 2015-08-24T03:43:03.237 に答える
0

http://www.mssqltips.com/tip.asp?tip=1662を参照してください

于 2010-05-04T14:54:42.977 に答える
0

なぜこれらのファイルをデータベースに入れるのですか?http / https接続がある場合は、ファイルをサーバーにアップロードし、保護されたディレクトリに書き込み、それらのファイルを一覧表示するページを作成し、ダウンロードするためのリンクを提供します。追加情報を保存したい場合は、データベースに書き込むことができます。サーバー側でファイルの名前を変更する必要があります(一意の名前を使用してください)。

于 2010-06-03T12:29:40.343 に答える
0

いくつかの調査の結果、意味がないと結論付けました。2005年のサポートを終了し、2008年のフィールドストリーム機能を使用することをお勧めします。2005年と2008年のどちらかを選択し、2005年にのみファイルストリームを使用する条件付きロジックを使用できます。

于 2010-06-03T13:39:07.403 に答える