You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

xp_cmdshell调用含PUT命令的FTP脚本时查询挂起问题求助

排查SQL存储过程执行FTP传输时挂起的问题

问题背景

我编写了一个SQL存储过程,用于备份数据库、压缩文件后通过FTP传输至远程服务器,代码如下:

CREATE PROCEDURE BackupAndTransferViaFTP
    @ftpServer VARCHAR(255),
    @ftpUsername VARCHAR(255),
    @ftpPassword VARCHAR(255),
    @databaseName NVARCHAR(255)
AS
BEGIN
    BEGIN TRY

        -- 启用xp_cmdshell
        EXEC sp_configure 'show advanced options', 1;
        RECONFIGURE;
        EXEC sp_configure 'xp_cmdshell', 1;
        RECONFIGURE;

        DECLARE @localBackupPath NVARCHAR(255)
        SET @localBackupPath = 'D:\Thon\Backup\' + @databaseName + 'Backup.bak';

        DECLARE @zipBackupPath NVARCHAR(255)
        SET @zipBackupPath = 'D:\Thon\Backup\' + @databaseName + 'Backup.zip';

        -- 备份数据库
        DECLARE @backupCommand NVARCHAR(1000)
        SET @backupCommand = 'BACKUP DATABASE ' + QUOTENAME(@databaseName) + ' TO DISK = ''' + @localBackupPath + '''';
        EXEC sp_executesql @backupCommand;

        -- 压缩备份文件
        DECLARE @cmd NVARCHAR(1000);
        SET @cmd = 'powershell.exe Compress-Archive -Path "' + @localBackupPath + '" -DestinationPath "' + 'D:\Thon\Backup\' + @databaseName + 'Backup.zip"';
        EXEC xp_cmdshell @cmd, NO_OUTPUT;

        -- 删除原始备份文件
        DECLARE @delBackupfile NVARCHAR(1000);
        SET @delBackupfile = 'DEL ' + @localBackupPath ;
        EXEC xp_cmdshell @delBackupfile, NO_OUTPUT;

---FTP传输部分---------------------------------------------------------
        DECLARE @ftpScriptFilePath NVARCHAR(255)
        SET @ftpScriptFilePath = 'D:\Thon\Backup\' + @databaseName + 'Backup_FTP_Script.txt';

        -- 转义特殊字符
        select @ftpserver = replace(replace(replace(@ftpserver, '|', '^|'),'<','^<'),'>','^>')
        select @ftpUsername = replace(replace(replace(@ftpUsername, '|', '^|'),'<','^<'),'>','^>')
        select @ftpPassword = replace(replace(replace(@ftpPassword, '|', '^|'),'<','^<'),'>','^>')
        select @zipBackupPath = replace(replace(replace(@zipBackupPath, '|', '^|'),'<','^<'),'>','^>')

        -- 生成FTP脚本文件
        DECLARE @Command NVARCHAR(2000)
        SET @Command = 'ECHO open ' + @ftpserver + '>' + @ftpScriptFilePath + '&'
            + 'ECHO ' + @ftpUsername + '>>' + @ftpScriptFilePath + '&'
            + 'ECHO ' + @ftpPassword + '>>' + @ftpScriptFilePath + '&'
            + 'ECHO prompt ' + '>>' + @ftpScriptFilePath + '&'
            + 'ECHO binary ' + '>>' + @ftpScriptFilePath + '&'
            + 'ECHO put ' + @zipBackupPath + '>>' + @ftpScriptFilePath + '&'
            + 'ECHO quit' + '>>' + @ftpScriptFilePath;
        EXEC xp_cmdshell @Command, NO_OUTPUT;
 
        -- 执行FTP命令
        DECLARE @ftpCommand NVARCHAR(1000)
        SET @ftpCommand = 'ftp -s:"' + @ftpScriptFilePath + '"';
        EXEC master..xp_cmdshell @ftpCommand;

        -- 关闭xp_cmdshell
        EXEC sp_configure 'xp_cmdshell', 0;
        RECONFIGURE;
        EXEC sp_configure 'show advanced options', 0;
        RECONFIGURE;

    END TRY
    BEGIN CATCH
        PRINT ERROR_MESSAGE();
    END CATCH;
END;

生成的FTP脚本内容如下:

open "abc.com"
"user"
"password"
prompt
binary
put "D:\Thon\Backup\hBackup.zip"
quit

当前遇到的问题:

  • 执行存储过程时,查询长时间挂起且无法取消,仅能通过任务管理器结束ftp.exe File Transfer Program进程终止
  • 移除FTP脚本生成语句中的+ 'ECHO put ' + @zipBackupPath + '>>' + @ftpScriptFilePath + '&'行后,存储过程可正常运行
  • 直接在命令提示符中执行对应的@ftpCommand命令,能正常完成传输

排查方向

1. xp_cmdshell输出缓冲区阻塞

执行FTP命令的xp_cmdshell未添加NO_OUTPUT参数,FTP传输产生的大量输出可能填满SQL Server缓冲区导致阻塞。修改执行FTP命令的语句:

EXEC master..xp_cmdshell @ftpCommand, NO_OUTPUT;

2. FTP脚本的引号解析错误

生成的FTP脚本中,服务器地址、用户名、密码都被添加了多余的"转义引号,FTP客户端解析时可能进入等待输入的异常状态。调整脚本生成逻辑,仅对put命令的路径添加引号:

SET @Command = 'ECHO open ' + @ftpserver + '>' + @ftpScriptFilePath + '&'
    + 'ECHO ' + @ftpUsername + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO ' + @ftpPassword + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO prompt ' + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO binary ' + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO put "' + @zipBackupPath + '">>' + @ftpScriptFilePath + '&'
    + 'ECHO quit' + '>>' + @ftpScriptFilePath;

生成的正确脚本格式应为:

open abc.com
user
password
prompt
binary
put "D:\Thon\Backup\hBackup.zip"
quit

3. SQL Server服务账号权限不足

手动执行FTP命令使用的是当前用户权限,而存储过程依托SQL Server服务账号运行,该账号可能没有读取备份压缩文件的权限,导致FTP传输卡住。检查:

  • SQL Server服务运行账号对D:\Thon\Backup目录的读权限
  • 确认压缩文件的所有者和权限设置

4. FTP主动模式的网络阻塞

默认FTP客户端使用主动模式,遇到防火墙或NAT时会建立连接失败,导致进程挂起。在FTP脚本中添加passive命令启用被动模式:

SET @Command = 'ECHO open ' + @ftpserver + '>' + @ftpScriptFilePath + '&'
    + 'ECHO ' + @ftpUsername + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO ' + @ftpPassword + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO prompt ' + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO binary ' + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO passive ' + '>>' + @ftpScriptFilePath + '&'
    + 'ECHO put "' + @zipBackupPath + '">>' + @ftpScriptFilePath + '&'
    + 'ECHO quit' + '>>' + @ftpScriptFilePath;

5. 路径转义字符导致文件识别失败

对@zipBackupPath做的特殊字符转义(如|转成^|),会让FTP客户端无法识别真实文件路径,导致找不到文件而进入等待状态。可去掉不必要的转义逻辑,仅对路径中的空格等必须转义的字符处理。


内容的提问来源于stack exchange,提问作者Thôn Mai Gia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 18:45:17