我正在创建一个程序集文件,以便可以从SQL Server中将get文件和put文件作为存储过程调用。下面是put文件的方法。当我通过SqlBoolean DeleteLocalFolder=true
时,WinSCP错误输出以下错误消息:
错误:WinSCP.SessionRemoteException:删除文件系统错误时出错。代码:5
[SqlProcedure]
public static void PutFiles(SqlString HostName, SqlString UserName, SqlString Password,
SqlString SshHostKeyFingerprint, SqlString SshPrivateKeyPath,
SqlString SshPrivateKeyPassphrase, SqlString LocalFolderPath,
SqlString RemoteFolderPath,SqlBoolean DeleteLocalFolder,
SqlString FileMask, out SqlString strMessage)
{
// Declare Variables to hold the input parameter values
string hostName = HostName.Value;
string userName = UserName.Value;
string password = Password.Value;
string sshFingerPrint = SshHostKeyFingerprint.Value;
string sshPrivateKey = SshPrivateKeyPath.Value;
string sshPassPhrase = SshPrivateKeyPassphrase.Value;
bool delateLocalFolder = DeleteLocalFolder.Value;
//Local Directory
DirectoryInfo dir = new DirectoryInfo(LocalFolderPath.Value);
string folderName = dir.Name;
//Begin connection to SFTP **Always uses default SFTP port
try
{
string FtpFolderPath = RemoteFolderPath.Value;
SessionOptions options = new SessionOptions()
{
Protocol = Protocol.Sftp,
HostName = hostName,
UserName = userName,
Password = password,
SshHostKeyFingerprint = sshFingerPrint,
SshPrivateKeyPath = sshPrivateKey,
SshPrivateKeyPassphrase = sshPassPhrase
};
//Open the Remote session
using (Session session = new Session())
{
session.ExecutablePath = ExecutablePath+"WinSCP.exe";
session.SessionLogPath = ExecutablePath+"WinscpLog.log";
session.DisableVersionCheck = true;
session.Open(options);
session.ExecutableProcessUserName = "username to log into the sql server instance";
SecureString _Password = new SecureString();
foreach (char _PasswordChar in "password to log in sql instance")
{
_Password.AppendChar(_PasswordChar);
}
session.ExecutableProcessPassword = _Password;
// Upload files
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
transferOptions.FileMask = FileMask.Value;
TransferOperationResult transferoperationalResult;
transferoperationalResult = session.PutFiles(LocalFolderPath.Value, FtpFolderPath, false, transferOptions);
transferoperationalResult.Check();
if (transferoperationalResult.IsSuccess)
{
if (dir.Exists == true)
{
string sourcePath = FtpFolderPath + folderName + "//";
session.MoveFile(sourcePath + "*.*", FtpFolderPath);
session.RemoveFiles(sourcePath);
}
}
}
strMessage = "Upload of files successful";
}
catch (Exception e)
{
//Console.WriteLine("Error: {0}", e);
strMessage = "Error: "+ e;
}
}
我在SSIS脚本任务中编写了自定义代码,并通过传递参数Session.PutFiles
method调用了bool remove = true
。它可以解决任何问题。任何人都可以解释为什么它只在自定义程序集中出错?
默认情况下,到达SQL Server外部的进程使用MSSQLSERVER(或MSSQL.InstanceName)服务的服务帐户的安全上下文。使用此默认设置时,此服务帐户需要具有对文件所在文件夹的写入/修改访问权限(如果文件权限使用继承,则可能对文件本身具有写入/修改权限)。
或者,您可以使用模拟将外部进程的安全上下文更改为执行存储过程的Login(在SQL Server中)的安全上下文,但前提是Login是Windows登录。 SQL Server登录没有操作系统已知的SID,因此它们的安全上下文将使用默认值,该默认值也是主SQL Server数据库引擎进程的服务帐户的默认值。
using System.Security.Principal;
public class stuff
{
[SqlProcedure]
public static void PutFiles()
{
using (WindowsImpersonationContext __ImpersonationIdentity =
SqlContext.WindowsIdentity.Impersonate())
{
... {do stuff} ...
__ImpersonationIdentity.Undo();
}
}
}
此外,Martin Prikryl评论这个答案提到:
WinSCP .NET程序集支持自己的模拟。见Session.ExecutableProcessUserName和Session.ExecutableProcessPassword
这个想法是(我假设,虽然我还没有测试过):
using System.Security;
...
using (Session session = new Session())
{
session.ExecutableProcessUserName = "some_user_name";
SecureString _Password = new SecureString();
foreach (char _PasswordChar in "some_password")
{
_Password.AppendChar(_PasswordChar);
}
session.ExecutableProcessPassword = _Password;
... other session stuff...
session.Open(options);
...more stuff...
_Password.Dispose();
} /* using() */
在上面的示例中,用户名和密码是字符串文字,但实际上这些值可以来自存储过程的输入参数,甚至来自sqlservr.exe.Config
文件。或者由于大会有一个PERMISSION_SET
的UNSAFE
以引用COM对象,你可以真正从任何地方获取值,包括注册表等。
所以也许这是尝试的东西。我不确定何时生效,所以尝试一个选项,如果它不起作用,那么尝试另一个。
请注意,至少,选项之间的区别是: