NMSSH上传文件出现16384字节倍数截断问题求助
SFTP上传文件偶尔被截断为16384字节倍数的问题排查与解决
我使用NMSSH实现服务器文件上传,代码如下:
let localURL = ... let sshSession = NMSSHSession.connect(toHost: "MYHOST", withUsername: "MYUSERNAME") if sshSession.isConnected { sshSession.authenticate(byPassword: "MYPASSWORD") if sshSession.isAuthorized { let sftpSession = NMSFTP(session: sshSession) sftpSession.connect() if sftpSession.isConnected { let data = try? Data(contentsOf: localURL) sftpSession.writeContents(data, toFileAtPath: "./uploads/MYFILE") // 能否刷新缓冲区? let command = script.sh // 处理上传的数据并生成待下载文件 let stderr: NSErrorPointer = nil sshSession.channel.execute(command, error: stderr) ... let jsonData = sftpSession.contents(atPath: path) ... sftpSession.disconnect() } else { // sftpSession未连接 } } else { // sshSession未授权 } sshSession.disconnect() } else { // sshSession未连接 }
应用上线数月后,发现约1%的上传文件被截断为16384字节的倍数(如16384、32768等)。该问题无法稳定复现:多次上传同一30000字节左右的测试文件均正常,但偶尔服务器上的文件仅保留前16384字节。我猜测可能是sftpSession断开前有缓冲数据未完全刷新,但不确定解决办法,求助问题原因。
更新(Kenster提醒后)
我使用标准Swift代码从Web服务获取数据再上传,代码如下:
let sessionConfig = URLSessionConfiguration.default let session = URLSession(configuration: sessionConfig) let request = URLRequest(url: remoteURL) let _ = session.downloadTask(with: request) { (data, response, error) in if let data = data, error == nil { do { try? FileManager.default.removeItem(at: localURL) try FileManager.default.copyItem(at: data, to: localURL) ...
未发现这段代码存在问题,且用户数据来自不同Web服务,因此Web服务并非问题根源。
更新2
我注意到默认的sshSession.channel.bufferSize和sftpSession.bufferSize均为16384,与截断文件的大小或其倍数一致。
补充上传后的操作流程:上传文件后通过script.sh处理,再下载处理生成的.json文件。现提出两个疑问:
- 在同一sshSession内完成上传、处理、下载的流程是否允许?
- 调用
sftpSession.writeContents()上传后,是否可以刷新缓冲区?
问题原因与解决方案
核心原因
缓冲区未完全刷新是问题的关键:NMSSH的sftpSession.writeContents()依赖SFTP协议的批量写入机制,默认使用16384字节的缓冲区。如果写入完成后立刻执行处理脚本或断开连接,缓冲区中剩余的未发送数据可能还未推送到服务器,导致文件被截断。同时,直接执行处理脚本会读取未完整落地的文件,进一步引发后续流程错误。
解决方案
- 强制刷新缓冲区:
- 若
writeContents未自动关闭文件句柄,调用sftpSession.closeFile(atPath: "./uploads/MYFILE"),关闭文件会触发缓冲区数据全部写入服务器。 - 直接调用底层libssh2的
sftp_flush函数(需导入libssh2桥接文件):if let sftpPtr = sftpSession.sftpSession { libssh2_sftp_flush(sftpPtr) }
- 若
- 校验文件完整性:执行处理脚本前,通过SFTP获取服务器上文件的大小,与本地文件大小对比,确认一致后再执行脚本。
- 调整缓冲区大小(可选):增大
sftpSession.bufferSize可降低截断概率,但无法从根本上解决问题,核心仍需确保数据完全写入。
疑问解答
- 同一sshSession内完成上传、处理、下载的流程完全允许:SSH协议支持多路复用,多个通道(SFTP通道、命令执行通道)可在同一个会话上串行或并行工作,无技术限制。
- 调用
writeContents()后可以刷新缓冲区:通过关闭文件句柄或调用底层sftp_flush函数,均可强制推送缓冲数据到服务器。
内容的提问来源于stack exchange,提问作者geohei
相关产品推荐
相关产品推荐

