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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 19:40:33