如何在Java中实现SFTP服务器文件解压?解决内容不完整报错问题
问题分析与解决方案
错误原因
你遇到的RequestQueue: unknown request id异常,本质是JSch的ChannelSftp单通道无法同时处理读、写请求。你在同一个通道上一边用channelSftp.get()读取ZIP文件,一边用channelSftp.put()写入解压后的文件,会打乱SFTP的请求队列,导致请求ID匹配失败,同时也会造成数据传输不完整,出现文件内容缺失。
修复方案:先下载到本地临时目录再解压上传
Lambda环境提供了/tmp临时目录,可以先把ZIP文件下载到本地,解压后再上传到SFTP服务器,这样就避免了单通道同时读写的冲突。示例代码如下:
import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.LsEntry; import java.io.*; import java.util.Vector; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class SftpUnzipExample { public void unzipOnSftp(ChannelSftp channelSftp) throws Exception { Vector filelist = channelSftp.ls("ML_E_DAM_D_op_files_*.zip"); String tempDir = "/tmp"; // Lambda临时目录 for (Object obj : filelist) { LsEntry entry = (LsEntry) obj; String zipFileName = entry.getFilename(); System.out.println("Processing zip file: " + zipFileName); // 1. 将ZIP文件下载到本地临时目录 File tempZip = new File(tempDir, zipFileName); try (OutputStream localOut = new FileOutputStream(tempZip)) { channelSftp.get(zipFileName, localOut); } // 2. 本地解压ZIP文件 try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(tempZip))) { ZipEntry zipEntry; while ((zipEntry = zipIn.getNextEntry()) != null) { if (!zipEntry.isDirectory()) { String entryName = zipEntry.getName(); File tempFile = new File(tempDir, entryName); // 创建父目录(如果需要) new File(tempFile.getParent()).mkdirs(); // 写入本地临时文件 try (OutputStream localFileOut = new FileOutputStream(tempFile)) { byte[] buffer = new byte[2048]; int len; while ((len = zipIn.read(buffer)) != -1) { localFileOut.write(buffer, 0, len); } } // 3. 将解压后的文件上传到SFTP服务器 try (InputStream localIn = new FileInputStream(tempFile)) { channelSftp.put(localIn, entryName); } // 删除本地临时文件(可选,节省空间) tempFile.delete(); } zipIn.closeEntry(); } } // 删除本地临时ZIP文件 tempZip.delete(); } } }
替代方案:使用两个独立的SFTP通道
如果不想使用本地临时目录,可以创建两个独立的ChannelSftp实例,一个专门负责读取ZIP文件,另一个负责写入解压后的文件,这样两个通道的请求队列互不干扰:
import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Session; import com.jcraft.jsch.LsEntry; import java.io.*; import java.util.Vector; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class DualChannelSftpUnzip { public void unzipWithDualChannels(String host, int port, String user, String password) throws Exception { JSch jsch = new JSch(); Session session = jsch.getSession(user, host, port); session.setPassword(password); session.setConfig("StrictHostKeyChecking", "no"); session.connect(); // 创建两个独立的SFTP通道 ChannelSftp readChannel = (ChannelSftp) session.openChannel("sftp"); ChannelSftp writeChannel = (ChannelSftp) session.openChannel("sftp"); readChannel.connect(); writeChannel.connect(); Vector filelist = readChannel.ls("ML_E_DAM_D_op_files_*.zip"); byte[] buffer = new byte[2048]; for (Object obj : filelist) { LsEntry entry = (LsEntry) obj; String zipFileName = entry.getFilename(); System.out.println("Processing zip file: " + zipFileName); try (ZipInputStream zipIn = new ZipInputStream(readChannel.get(zipFileName))) { ZipEntry zipEntry; while ((zipEntry = zipIn.getNextEntry()) != null) { if (!zipEntry.isDirectory()) { try (OutputStream sftpOut = writeChannel.put(zipEntry.getName())) { int len; while ((len = zipIn.read(buffer)) != -1) { sftpOut.write(buffer, 0, len); } } } zipIn.closeEntry(); } } } // 关闭通道和会话 readChannel.disconnect(); writeChannel.disconnect(); session.disconnect(); } }
额外注意事项
- 务必使用try-with-resources语法自动关闭流和通道,避免资源泄漏,尤其是在Lambda环境中,资源泄漏可能导致后续执行异常。
- Lambda的
/tmp目录有存储上限(默认512MB),如果ZIP文件过大,建议使用双通道方案。 - 操作完成后记得清理临时文件,避免占用Lambda的临时存储空间。
内容的提问来源于stack exchange,提问作者Shatakshi
相关产品推荐
相关产品推荐

