jsch连接sftp失败主因是认证方式不匹配,如私钥格式不兼容(需转为pkcs#1)、路径未用绝对路径、密码认证未前置调用或stricthostkeychecking未禁用;上传xml乱码源于编码不一致,应确保utf-8无bom保存并显式指定编码读取;大文件需设超时、每次put后必须channel.exit();路径拼接须normalize并过滤非法字符。

JSch连接SFTP时抛出 com.jcraft.jsch.JSchException: Auth fail
根本原因通常是认证方式不匹配,不是密码错了就是私钥格式/权限/路径没配对。JSch默认不读取OpenSSH的id_rsa新格式(即PEM头为-----BEGIN OPENSSH PRIVATE KEY-----),只支持老式PKCS#1(-----BEGIN RSA PRIVATE KEY-----)。
- 用
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa把密钥转成兼容格式 - 加载私钥时必须调用
jsch.addIdentity(),且路径是本地绝对路径(相对路径容易静默失败) - 如果用密码登录,确认
session.setPassword()在session.connect()前调用,且服务端允许密码认证(很多生产环境禁用) - 别漏掉
session.setConfig("StrictHostKeyChecking", "no"),否则首次连接会因未知host key卡住
上传XML文件时中文乱码或内容被截断
JSch的SftpChannel.put()本身不处理字符编码,它传的是字节流。所谓“乱码”,其实是本地XML文件保存编码(如UTF-8 BOM)和程序读取方式不一致导致的写入失真。
- 确保XML文件以UTF-8无BOM保存(BOM会导致
<?xml前面多三个字节,部分解析器报错) - 不要用
FileInputStream直接塞给put()——它没问题,但如果你先用String.getBytes()再构造ByteArrayInputStream,必须显式指定"UTF-8",否则依赖平台默认编码 - 上传前用
Files.readAllBytes(Paths.get("data.xml"))最稳妥,它按文件实际字节读,不涉及字符串解码 - 检查目标Linux服务器的挂载选项:如果SFTP目录挂的是NTFS或FAT32分区,可能不支持UTF-8文件名(但内容不受影响)
SftpChannel.put()阻塞、超时或抛java.io.IOException: Pipe closed
这不是网络断开,而是JSch内部管道状态不同步。常见于未正确关闭流、并发操作channel、或大文件传输中途异常退出后channel残留。
- 每次上传必须配对使用
channel.put(...)+channel.exit(),不能只靠session.disconnect()收尾 - 上传大XML(>10MB)时,在
session.connect()后加session.setTimeout(60_000),避免默认10秒超时中断 - 别在同一个
SftpChannel实例上连续调用多次put()——每次都要channel.exit()再session.openChannel("sftp")重开 - 捕获
SftpException时,检查e.id:4是SSH_FX_FAILURE(路径不存在),9是SSH_FX_PERMISSION_DENIED(目标目录不可写)
Java里怎么安全拼接XML路径并防止覆盖同名文件
XML文件名常含时间戳或业务ID,但直接字符串拼接易引入路径遍历漏洞(比如../config.xml)或非法字符(Windows下:、*等)。SFTP服务端通常不校验文件名合法性,全由客户端负责。
立即学习“Java免费学习笔记(深入)”;
- 用
Paths.get("upload", "order", id + ".xml").normalize().toString()处理路径,自动清理..和冗余/ - 文件名中过滤控制字符:
fileName.replaceAll("[\x00-\x1f\x7f]", "_"),再替换[^a-zA-Z0-9._-]为- - 上传前用
channel.stat()检查目标是否存在,存在则用channel.rename()备份,或追加时间戳重命名 - 别用
channel.cd()切目录再put()——它不改变上传路径语义,put()第一个参数永远是远程绝对路径或相对于用户home的路径
channel.exit()的调用时机——前者导致连不上,后者导致后续上传莫名失败,而且错误信息完全不相关。










