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

使用JSCH通过SFTP上传文件至远程服务器时如何展示上传进度

Web服务器通过JSch上传文件到远程SFTP阶段进度条实现方案

你卡壳的核心原因是两个上传阶段的进度采集逻辑完全不同:前端到Web服务器的进度由浏览器侧监听HTTP上传事件采集,Web服务器到远程SFTP的进度需要在服务端采集,再通过通信通道同步给前端,具体落地步骤如下:

一、JSch侧进度采集实现

JSch原生提供了进度监听接口,直接实现SftpProgressMonitor即可拿到实时传输字节数,不需要自己包装流做计数:

  • 自定义进度监听器,每批次数据传输完成后计算进度,存入可跨请求访问的缓存
import com.jcraft.jsch.SftpProgressMonitor;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

public class SftpUploadProgressMonitor implements SftpProgressMonitor {
    private final String taskId;
    private long totalFileSize = 0;
    private final AtomicLong transferredBytes = new AtomicLong(0);

    // 每个上传任务生成唯一taskId,用于和前端请求做匹配
    public SftpUploadProgressMonitor(String taskId, long localFileSize) {
        this.taskId = taskId;
        this.totalFileSize = localFileSize;
    }

    @Override
    public void init(int op, String src, String dest, long max) {
        // 部分场景下JSch会自动识别文件总大小,覆盖传入的初始值
        this.totalFileSize = max > 0 ? max : this.totalFileSize;
        transferredBytes.set(0);
        // 初始化时写入进度0
        ProgressCache.set(taskId, 0);
    }

    @Override
    public boolean count(long batchBytes) {
        long currentTransferred = transferredBytes.addAndGet(batchBytes);
        int progress = (int) Math.min(100, (currentTransferred * 100) / totalFileSize);
        // 将当前进度写入缓存,注意缓存需设置过期时间(建议30~60分钟),避免内存泄漏
        ProgressCache.set(taskId, progress);
        // 返回false可主动中断传输
        return true;
    }

    @Override
    public void end() {
        // 传输完成强制写入100%进度
        ProgressCache.set(taskId, 100);
    }

    // 对外暴露生成taskId的工具方法
    public static String generateTaskId() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}
  • 上传SFTP时注入自定义监听器,不要调用无监听器的重载方法
// 前端文件上传到Web服务器本地临时目录完成后,先生成唯一任务ID
String sftpTaskId = SftpUploadProgressMonitor.generateTaskId();
// 将taskId先返回给前端,前端拿到后启动进度查询/监听
try {
    channelSftp.put(
        new FileInputStream(localTempFile),
        remoteTargetPath,
        new SftpUploadProgressMonitor(sftpTaskId, localTempFile.length()),
        ChannelSftp.OVERWRITE
    );
} catch (Exception e) {
    // 传输异常时写入失败状态,供前端识别
    ProgressCache.set(sftpTaskId, -1);
    throw e;
}

注意:如果你的服务是多实例部署,不要用本地内存做进度缓存,替换为Redis这类集中式缓存,避免前端进度查询请求打到其他实例时拿不到数据

二、前后端进度同步方案

根据你的项目并发量和技术栈二选一即可:

方案1:短轮询(实现成本最低,适合低并发场景)

  • 前端完成第一阶段(前端→Web服务器)上传,拿到返回的sftpTaskId后,启动定时器,每300ms发一次GET请求,携带taskId查询当前SFTP传输进度
  • 后端提供进度查询接口,根据taskId从缓存中读取进度值返回,包含三种状态:传输中(0~99的数值)、完成(100)、失败(-1)
  • 前端拿到进度值后更新进度条,当进度为100或-1时,清除定时器,展示对应结果
    轮询间隔不要小于100ms,否则会给服务端增加不必要的请求压力

方案2:SSE推送(实时性好,适合中高并发场景)

SSE是HTTP协议自带的单向服务端推送能力,实现成本比WebSocket低,完全适配进度上报场景:

  • 前端拿到sftpTaskId后,调用SSE连接接口,和服务端建立长连接,绑定当前taskId
  • 后端监听器的count方法更新进度时,直接将进度值通过SSE连接推送给对应前端,不需要前端反复发请求
  • 传输完成/失败/连接超时后,主动关闭SSE连接即可

三、常见踩坑

  • 不要直接调用channelSftp.put(fileInputStream, remotePath)这类不带进度监听器的重载方法,这类方法不会触发进度回调
  • 进度计算时要做边界判断,避免总文件大小为0时触发除零异常
  • 大文件分片上传到SFTP的场景,需要累加所有分片的传输字节数计算总进度,不要只统计单个分片的进度
  • 缓存必须设置过期时间,避免传输中断的任务残留数据占用内存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:06:14