SQL Server: كيفية نسخ ملف (PDF ، DOC ، TXT ...) المخزنة في حقل Varbinary (Max) إلى ملف في إجراء مخزن CLR؟

StackOverflow https://stackoverflow.com/questions/2766314

سؤال

أطرح هذا السؤال على أنه متابعة هذا السؤال.

تم نشر الحل الذي يستخدم BCP و XP_CMDSHELL ، وهذا ليس الحل المطلوب ، هنا.

أنا جديد على C# (بما أنني مطور Delphi) على أي حال تمكنت من إنشاء إجراء مخزن CLR بسيط باتباع برنامج تعليمي.

تتمثل مهمتي في نقل ملف من نظام ملفات العميل إلى نظام ملفات الخادم (يمكن الوصول إلى الخادم باستخدام IP عن بُعد ، لذلك لا يمكنني استخدام مجلد مشترك كوجهة ، وهذا هو السبب في أنني بحاجة إلى إجراء تخزين CLR).

لذلك أخطط ل:

  1. تخزين من Delphi الملف في عمود Varbinary (Max) من جدول مؤقت
  2. اتصل بإجراء CLR المخزن لإنشاء ملف في المسار المطلوب باستخدام البيانات الواردة في حقل Varbinary (Max)

تخيل أنني بحاجة إلى نقل c: myfile.pdf إلى z: myfile.pdf ، حيث c: هو صلب على النظام المحلي و z: هو harddrive على الخادم. C في نيويورك ، Z موجود في لندن ولا يوجد VPN بينهما ، فقط اتصال HTTPS.

أقوم بتقديم الكود أدناه (لا يعمل) بحيث يمكن لشخص ما تعديله لجعله يعمل؟ هنا أفترض أن يكون لديك جدول يسمى mytable مع حقلين: id (int) والبيانات (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 حتى لا أحتاج إلى استخدام جدول مؤقت؟)

إذا كانت إجابة التكوين الفرعي لا ، هل يمكنك وصف نهج تجنب جدول مؤقت؟ فهل هناك طريقة أفضل ثم الطريقة التي أصفها أعلاه (= جدول مؤقت + إجراء مخزن)؟ طريقة لتمرير DataAstream مباشرة من تطبيق العميل إلى الإجراء المخزن CLR؟ (يمكن أن تكون ملفاتي أي حجم ولكنها كبيرة جدًا)

هل كانت مفيدة؟

المحلول 4

بعد بعض الأبحاث ، استنتج أنه لا معنى له ، من الأفضل إسقاط دعم عام 2005 واستخدام ميزة 2008 Fielstream. يمكنني الحصول على منطق مشروط للاختيار بين عامي 2005 و 2008 واستخدام Filestream فقط لعام 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 من EXTERNAL_ACCESS.

في كلتا الحالتين ، ستنفذها بالطريقة التالية:

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

و "0x2a20202a" يجب أن يمنحك ملفًا يحتوي على 4 أحرف 4 (العلامة النجمية ، المساحة ، الفضاء ، العلامة النجمية):

* *

لماذا تضع هذه الملفات في قاعدة البيانات؟ إذا كان لديك اتصال HTTP/HTTPS ، فيمكنك تحميل الملف على الخادم ، والكتابة في dierctory محمي وإنشاء صفحة لسرد هذه الملفات وإعطاء رابط لتنزيله. إذا كنت ترغب في تخزين بعض المعلومات الإضافية ، فيمكنك كتابتها في قاعدة البيانات. تحتاج فقط إلى تغيير اسم الملف على جانب الخادم (استخدم اسمًا فريدًا).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top