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

JSCH实现SFTP传文件到SharePoint报InputStream已关闭异常排查

SFTP传输文件到SharePoint时抛出InputStream已关闭异常

问题背景

我使用JSCH库建立SFTP连接,尝试将SFTP服务器上的文件传输至SharePoint文件夹。
核心上传逻辑代码如下:

private void sharePointUpload(String driveId, String targetLocation, InputStream inputStream, String fileName) throws FileNotFoundException, IOException {
    UploadSession uploadSession = this.graphServiceClient.drives().byId(driveId).root().itemWithPath(targetLocation +"/"+ fileName)
                                    .createUploadSession(this.uploadParams).buildRequest().post();
    byte[] inputStreamByteArray = IOUtils.toByteArray(inputStream);
    LargeFileUploadTask<DriveItem> largeFileUploadTask = new LargeFileUploadTask<DriveItem>
                                        (uploadSession, this.graphServiceClient, new ByteArrayInputStream(inputStreamByteArray), inputStreamByteArray.length, DriveItem.class);
    largeFileUploadTask.upload(MAXIMUM_CHUNK_SIZE);
}

代码执行到IOUtils.toByteArray(inputStream)行时,抛出java.io.IOException: inputstream is closed异常。

错误日志

错误日志截图

补充信息

获取SFTP文件流的循环逻辑代码片段如下:

for (int i = 0; i < list.size(); i++) {
    sourcefilenamelist = list.get(i);
    logger.log(proccessingFileLogText + sourcefilenamelist);
    if (targetFilenameflag.equalsIgnoreCase("false")) {
        targetFilename = sourcefilenamelist;
    }
    
    InputStream stream = sourceChannelSftp.get(sourceLocation + '/' + sourcefilenamelist);
    InputStreamDetails decryptEncryptInputStreamDetails = decryptEncryptInputStream(stream, targetFilename, sourceEncryption, targetEncryption, jobjsource);
    stream = decryptEncryptInputStreamDetails.getInputStream();
    targetFilename = decryptEncryptInputStreamDetails.getFileName();
    
    try {
        sharePointUpload(targetDriveId, targetLocation, stream, targetFilename);
    } catch (ClientException e) {
        logger.log(" Graph API ClientException : ");
        logger.log( "e.getMessage()     :- " + e.getMessage() );
        logger.log( "RootCauseMessage   :- " + ExceptionUtils.getRootCauseMessage(e) );
//                                      isSQSMessageRetry = true;
        throw e;
    }
// 省略其余循环内业务逻辑
...
...
...
}

实际运行现象:文件列表中仅有少量文件可正常传输,其余文件均触发上述流关闭异常。

根本原因

报错核心是JSCH返回的SFTP流生命周期和代码执行逻辑不匹配:

  1. ChannelSftp.get()返回的输入流不是独立的本地缓存流,它直接依附于当前SFTP连接通道。只要SFTP连接因空闲超时被回收、或者通道切换去执行下一个文件操作,之前获取的InputStream会被JSCH自动关闭。现有逻辑拿到流后没有立刻将内容读取到本地内存,而是直接把原始流透传给sharePointUpload方法——该方法第一步是发起网络请求调用Graph接口创建SharePoint上传会话,这个HTTP请求的耗时不可控,一旦耗时过长SFTP服务端就会断开空闲连接,对应的流自然被关闭。
  2. 中间调用的decryptEncryptInputStream加解密方法,如果内部读取了流内容却没有重置流指针、或者处理完成后提前关闭了流,也会触发同类报错。少量文件能传输成功,基本都是体积较小的文件,从获取流到读完全量字节的耗时极短,没有触发连接超时或流回收逻辑。

修复方案

  • 调整流处理逻辑:拿到SFTP流、完成加解密处理后,第一时间将流全量读取为字节数组,立即关闭SFTP相关的原始流,再基于字节数组新建ByteArrayInputStream传入SharePoint上传方法,彻底将上传流程和SFTP连接的生命周期解绑,避免上传的网络请求过程长时间占用SFTP连接。
  • 给SFTP通道配置合理的连接超时、读取超时参数,每处理完一个文件就检查通道连接状态,不要复用已断连的通道获取文件流。
  • 排查decryptEncryptInputStream方法实现,确认返回的输入流指针位于起始位置,不存在方法内部提前关闭流的逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:15:41