我一直在编写一个从存储过程调用的 sql clr udf,以将未知文件类型(jpg、doc、pdf 等)保存到文件系统。UFD 接受 SqlBytes 类型的参数,该参数从调用存储过程(这是文件 blob)传递一个 varbinary(max)。问题是,在 CLR UDF 的上下文中,我无法访问 value 属性,甚至无法访问 SqlBytes 文件参数的读取方法,因为它返回以下无效异常错误。
我缩短了我的 UDF 以突出提到的问题。任何帮助将非常感激。
提前致谢。
错误
在这种情况下不允许数据访问。上下文要么是未用 or 标记的函数或方法,DataAccessKind.Read
要么SystemDataAccessKind.Read
是从表值函数的 FillRow 方法获取数据的回调,要么是 UDT 验证方法。
堆栈跟踪
at System.Data.SqlServer.Internal.ClrLevelContext.CheckSqlAccessReturnCode(SqlAccessApiReturnCode eRc)
at System.Data.SqlServer.Internal.ClrLevelContext.XvarProxyRead(CClrXvarProxy* pXvarProxy, UInt64 iPosition, Byte* pbBuffer, UInt32 cbCount)
at System.Data.SqlServer.Internal.ClrLevelContext.System.Data.SqlServer.Internal.IXvarProxyAccessor.XvarProxyRead(CClrXvarProxy* , UInt64 , Byte* , UInt32 )
at System.Data.SqlServer.Internal.StreamOnBlobHandle.Read(Byte* pbBuffer, UInt64 offset, UInt32 count)
at System.Data.SqlServer.Internal.XvarBlobStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.BufferedStream.Read(Byte[] array, Int32 offset, Int32 count)
at System.Data.SqlTypes.SqlBytes.Read(Int64 offset, Byte[] buffer, Int32 offsetInBuffer, Int32 count)
at UserDefinedFunctions.SaveFileToFS(SqlBytes file, String fileName, String fileExtension, String path)
CLR 代码
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Security.Permissions;
using System.Security.Principal;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)]
public static SqlString SaveFileToFS(SqlBytes file)
{
WindowsImpersonationContext newContext = null;
WindowsIdentity newIdentity = SqlContext.WindowsIdentity;
try
{
if (newIdentity != null) newContext = newIdentity.Impersonate();
byte[] buffer = new byte[8040 * 4];
long offset = 0;
long read = 0;
//This file.Read will throw an error
read = file.Read(offset, buffer, 0, buffer.Length);
//this line will throw the same error
buffer = (byte[])file.Value;
catch (System.Exception ex1)
{
throw ex1;
}
finally
{
if (newContext != null) newContext.Undo();
}
return new SqlString("Success");
}
};
所以只是为了完成这个线程 - 这是一个 CLR UDF 的基本 POC 代码,它接受一个 varbinary(max) 文件 blob、文件名、文件扩展名和要写入的路径,然后将其保存到定义的文件系统位置(提供它具有适当的文件系统权限)。希望它可以帮助某人:-)
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Security.Permissions;
using System.Security.Principal;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read , SystemDataAccess = SystemDataAccessKind.Read )]
public static SqlString SaveFileToFS(SqlBytes file, string fileName, string fileExtension, string path)
{
WindowsImpersonationContext newContext = null;
WindowsIdentity newIdentity = SqlContext.WindowsIdentity;
try
{
long length = file.Length ;
byte[] buffer = file.Value;
long offset = 0;
long read = 0;
int times = 0;
if (newIdentity != null) newContext = newIdentity.Impersonate();
FileStream fs = new FileStream(path + fileName + fileExtension, System.IO.FileMode.Create, System.IO.FileAccess.Write);
while (length > 1000)
{
fs.Write(buffer, 1000 * times, 1000);
length -= 1000;
times++;
}
fs.Write(buffer, 1000 * times, (int)length);
fs.Close();
}
catch (System.Exception ex1)
{
throw ex1;
}
finally
{
if (newContext != null) newContext.Undo();
}
return new SqlString(string.Format("Saved file: {0}{1} to path: {2}", fileName, fileExtension, path));
}
};