JSCH实现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流生命周期和代码执行逻辑不匹配:
ChannelSftp.get()返回的输入流不是独立的本地缓存流,它直接依附于当前SFTP连接通道。只要SFTP连接因空闲超时被回收、或者通道切换去执行下一个文件操作,之前获取的InputStream会被JSCH自动关闭。现有逻辑拿到流后没有立刻将内容读取到本地内存,而是直接把原始流透传给sharePointUpload方法——该方法第一步是发起网络请求调用Graph接口创建SharePoint上传会话,这个HTTP请求的耗时不可控,一旦耗时过长SFTP服务端就会断开空闲连接,对应的流自然被关闭。- 中间调用的
decryptEncryptInputStream加解密方法,如果内部读取了流内容却没有重置流指针、或者处理完成后提前关闭了流,也会触发同类报错。少量文件能传输成功,基本都是体积较小的文件,从获取流到读完全量字节的耗时极短,没有触发连接超时或流回收逻辑。
修复方案
- 调整流处理逻辑:拿到SFTP流、完成加解密处理后,第一时间将流全量读取为字节数组,立即关闭SFTP相关的原始流,再基于字节数组新建
ByteArrayInputStream传入SharePoint上传方法,彻底将上传流程和SFTP连接的生命周期解绑,避免上传的网络请求过程长时间占用SFTP连接。 - 给SFTP通道配置合理的连接超时、读取超时参数,每处理完一个文件就检查通道连接状态,不要复用已断连的通道获取文件流。
- 排查
decryptEncryptInputStream方法实现,确认返回的输入流指针位于起始位置,不存在方法内部提前关闭流的逻辑。
内容的提问来源于stack exchange,提问作者Shantaram Tupe
相关产品推荐
相关产品推荐

