从 SQL Server 2005 存储过程检查文件是否存在的最佳方法是什么?
-
08-06-2019 - |
题
我们在 SQL Server 2000 中使用“未记录的”xp_fileexist 存储过程多年,并且没有遇到任何问题。2005 年,他们似乎稍微修改了行为,如果执行用户帐户不是系统管理员,则始终返回 0。如果 SQL Server 服务在 LocalSystem 帐户下运行并且您尝试检查网络上的文件,它似乎也会返回零。
我想摆脱 xp_fileexist。有没有人有更好的方法来从存储过程内部检查网络位置上文件是否存在?
解决方案
也许 CLR 存储过程正是您正在寻找的。当您需要以某种方式与系统交互时,通常会使用这些。
其他提示
您必须将 CLR 标记为 EXTERNAL_ACCESS 才能访问 System.IO 命名空间,但随着情况的发展,这并不是一个坏方法。
SAFE 是默认权限集,但限制性很强。通过 SAFE 设置,您只能访问本地数据库中的数据以对该数据执行计算逻辑。EXTERNAL_ACCESS 是权限层次结构中的下一步。此设置允许您访问外部资源,例如文件系统、Windows 事件查看器和 Web 服务。这种类型的资源访问在 SQL Server 2000 及更早版本中是不可能的。此权限集还限制影响程序集稳健性的操作,例如指针访问。UNSAFE 权限集假定程序集完全信任,因此不施加“代码访问安全”限制。此设置与扩展存储过程的功能方式相当——您假设所有代码都是安全的。但是,此设置确实将不安全程序集的创建限制为具有系统管理员权限的用户。Microsoft 建议您尽可能避免创建不安全的程序集。
我仍然相信 CLR 过程可能是最好的选择。所以,我接受这个答案。然而,要么我不那么聪明,要么它很难实现。我们的 SQL Server 服务在本地帐户下运行,因为根据 Mircosoft 的说法,这是让 iSeries 链接服务器从 64 位 SQL Server 2005 实例运行的唯一方法。当我们将 SQL Server 服务更改为使用域帐户运行时,xp_fileexist 命令对于位于网络上的文件可以正常工作。
我创建了这个 CLR 存储过程,并将其权限级别设置为“外部”并对其进行了签名:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Security.Principal;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void FileExists(SqlString fileName, out SqlInt32 returnValue)
{
WindowsImpersonationContext originalContext = null;
try
{
WindowsIdentity callerIdentity = SqlContext.WindowsIdentity;
originalContext = callerIdentity.Impersonate();
if (System.IO.File.Exists(Convert.ToString(fileName)))
{
returnValue = 1;
}
else
{
returnValue = 0;
}
}
catch (Exception)
{
returnValue = -1;
}
finally
{
if (originalContext != null)
{
originalContext.Undo();
}
}
}
}
然后我运行这些 TSQL 命令:
USE master
GO
CREATE ASYMMETRIC KEY FileUtilitiesKey FROM EXECUTABLE FILE = 'J:\FileUtilities.dll'
CREATE LOGIN CLRLogin FROM ASYMMETRIC KEY FileUtilitiesKey
GRANT EXTERNAL ACCESS ASSEMBLY TO CLRLogin
ALTER DATABASE database SET TRUSTWORTHY ON;
然后,我从 Visual Studio 将 CLR 存储过程部署到我的目标数据库,并使用此 TSQL 从通过 Windows 身份验证登录的 SSMS 执行:
DECLARE @i INT
--EXEC FileExists '\\\\server\\share\\folder\\file.dat', @i OUT
EXEC FileExists 'j:\\file.dat', @i OUT
SELECT @i
无论我尝试本地文件还是网络文件,我总是得到 0。我可能会稍后再试,但现在,我将尝试走另一条路。如果有人能提供一些启示,我们将不胜感激。
@Paul,该代码看起来应该可以工作。您是否尝试过在该方法中进行一些跟踪以确保 Convert.ToString(fileName)
不是以某种方式冲洗路径吗?