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

使用Paramiko/sshd-mina的SFTP上传远程服务器失败,重试却成功?

SFTP上传大小不匹配问题排查

问题背景

以下是用于SFTP文件上传的代码:

# 检查文件大小
def check_file_size(path):
    try:
        size = os.path.getsize(path)
        print(f"本地文件大小: {size} 字节")
        return size
    except OSError as e:
        print(f"获取{path}大小失败: {e}")
        return None

# 创建SFTP会话
def create_sftp_session(hostname, port, username, password):
    transport = paramiko.Transport((hostname, port))
    transport.connect(username=username, password=password)
    sftp = paramiko.SFTPClient.from_transport(transport)
    return sftp, transport

# 上传文件到远程服务器,存在则覆盖
def upload_file(sftp, local_path, remote_path, retries=3):
        try:
            # 创建临时文件并复制内容
            with tempfile.NamedTemporaryFile(delete=False) as temp_file:
                with open(local_path, 'r') as file:
                    temp_file.write(file.read().encode())
                temp_file_path = temp_file.name
            
            local_size = os.path.getsize(temp_file_path)
            sftp.put(temp_file_path, remote_path)
            remote_size = sftp.stat(remote_path).st_size
            print(f"上传后远程文件大小: {remote_size} 字节")
            if local_size != remote_size:
                print(f"警告: 上传后大小不匹配!")
            
            # 清理临时文件
            os.remove(temp_file_path)
        except IOError as e:
            print(f"上传{local_path}到{remote_path}失败: {e}")

# 删除本地文件
def delete_local_file(path):
    try:
        os.remove(path)
        print(f"本地文件{path}已删除。")
    except OSError as e:
        print(f"删除{path}失败: {e}")

# 主执行逻辑
def main():
    # 创建本地文件
    create_local_file(local_file_path)

    # 建立SFTP会话并上传文件
    sftp, transport = create_sftp_session(hostname, port, username, password)
    try:
        # 上传并覆盖文件
        upload_file(sftp, local_file_path, remote_file_path)
    finally:
        sftp.close()
        transport.close()

    # 删除本地文件
    delete_local_file(local_file_path)

if __name__ == "__main__":
    main()

针对远程服务器运行时,代码执行失败,报错:

Failed to upload localfile.txt to /dir/removefile.csv: size mismatch in put! 94 != 141

但本地Docker搭建的SFTP服务器、sshd-mina环境下均可正常运行,请问远程服务器为何无法正常工作?

另外,将upload_file函数修改为带重试逻辑的版本后:

# 上传文件到远程服务器,存在则覆盖
def upload_file(sftp, local_path, remote_path, retries=3):
    for attempt in range(retries):
        try:
            # 创建临时文件并复制内容
            with tempfile.NamedTemporaryFile(delete=False) as temp_file:
                with open(local_path, 'r') as file:
                    temp_file.write(file.read().encode())
                temp_file_path = temp_file.name
            
            local_size = os.path.getsize(temp_file_path)
            sftp.put(temp_file_path, remote_path)
            remote_size = sftp.stat(remote_path).st_size
            print(f"上传后远程文件大小: {remote_size} 字节")
            if local_size != remote_size:
                print(f"警告: 上传后大小不匹配!")
            
            # 清理临时文件
            os.remove(temp_file_path)
            break  # 上传成功则退出循环
        except IOError as e:
            print(f"第{attempt + 1}次上传{local_path}到{remote_path}失败: {e}")
            if attempt < retries - 1:
                print("重试中...")
                time.sleep(5)  # 等待后重试

第二次尝试即可成功执行,日志如下:

Local file localfile.txt created with content. Attempt 1 failed to
upload localfile.txt to /flex-selections/Portfolio.csv: size mismatch
in put! 94 != 141 Retrying... Remote file size after upload: 141
bytes Local file localfile.txt deleted.

请问这一现象为何会发生?


问题分析与解答

1. 远程服务器首次上传失败的原因

  • 网络传输不完整:远程网络存在延迟或丢包,sftp.put返回成功信号时,服务器并未完全接收所有数据,此时调用sftp.stat读取的是未写完的文件大小,和本地文件不一致。
  • 文件系统写缓存延迟:部分远程服务器的文件系统会用内存缓存暂存写入数据,并未立即刷到磁盘。这时stat读取的是缓存中的部分数据大小,不是最终完整文件的大小。
  • SFTP服务器额外处理延迟:企业级SFTP服务器可能对上传文件做病毒扫描、权限校验等额外处理,导致文件写入磁盘的过程被延迟,首次读取大小还未完成全量写入。
  • 旧文件残留数据:如果远程路径原本有同名文件且更大,覆盖时服务器文件系统未及时截断旧文件,stat返回的是旧文件的残留大小。

2. 重试后成功的原因

  • 等待时间让服务器完成写入:重试逻辑中的time.sleep(5)给了服务器足够时间,将缓存数据刷到磁盘,或是完成文件后续的扫描、旧文件截断等处理。
  • 第二次传输的完整性:重试时网络环境可能恢复稳定,数据完整传输;同时服务器已完成旧文件清理,新文件能完整写入并刷新到磁盘,此时stat读取的是正确的文件大小。

内容的提问来源于stack exchange,提问作者SGiux

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 06:05:02