OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3312|回复: 0

java通过sftp形式连接主机下载文件(附项目与主机编码不一致解决方法)

[复制链接]

143

主题

145

帖子

0

精华

高级会员

Rank: 4

积分
585
金钱
585
注册时间
2020-5-25
在线时间
42 小时
发表于 2020-8-26 16:28:35 | 显示全部楼层 |阅读模式
最近接了一个文件下载接口需求,需要采用sftp形式与对端主机连接,进行文件传输。
首先引入java操作sftp的工具类包:
    <dependency>        <groupId>com.jcraft</groupId>        <artifactId>jsch</artifactId>        <version>0.1.53</version>     </dependency>
编写文件工具类(包含sftp连接、断开、下载文件等方法)
/** * sftp形式下载文件 * * @Author wangshuai * */@Slf4jpublic class SFTPUtil {    private ChannelSftp sftp = new ChannelSftp();    private Session session;    /**     * SFTP 登录用户名     */    private String username;    /**     * SFTP 登录密码     */    private String password;    /**     * 私钥     */    private String privateKey;    /**     * SFTP 服务器地址IP地址     */    private String host;    /**     * SFTP 端口     */    private int port;    /**     * 构造基于密码认证的sftp对象     */    public SFTPUtil(String username, String password, String host, int port) {        this.username = username;        this.password = password;        this.host = host;        this.port = port;    }    /**     * 构造基于秘钥认证的sftp对象     */    public SFTPUtil(String username, String host, int port, String privateKey) {        this.username = username;        this.host = host;        this.port = port;        this.privateKey = privateKey;    }    public SFTPUtil() {    }    /**     * 连接sftp服务器     */    public void login() {        try {            JSch jsch = new JSch();            if (privateKey != null) {                jsch.addIdentity(privateKey);// 设置私钥            }            session = jsch.getSession(username, host, port);            if (password != null) {                session.setPassword(password);            }            Properties config = new Properties();            config.put("StrictHostKeyChecking", "no");            session.setConfig(config);            session.connect();            Channel channel = session.openChannel("sftp");            channel.connect();            sftp = (ChannelSftp) channel;                   } catch (JSchException e) {            e.printStackTrace();            log.error("login.faild",e);        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (NoSuchFieldException e) {            e.printStackTrace();        } catch (SftpException e) {            e.printStackTrace();        }    }    /**     * 关闭连接 server     */    public void logout() {        if (sftp != null) {            if (sftp.isConnected()) {                sftp.disconnect();            }        }        if (session != null) {            if (session.isConnected()) {                session.disconnect();            }        }    }    public boolean isExist(String serverPath, String folderName) {        try {            sftp.cd(serverPath);            log.info("登录到当前路径:"+serverPath);            SftpATTRS attrs = null;            attrs = sftp.stat(folderName);            if (attrs != null) {                return true;            }        } catch (Exception e) {            e.getMessage();            return false;        }        return false;    }    public boolean isConnect() {        if (null != session) {            return session.isConnected();        }        return false;    }    public InputStream download(String directory) throws SftpException {        return sftp.get(directory);    }}
编写对应controller控制层代码
@RestController@RequestMapping(value = "XXXXXXX")@Slf4jpublic class StatementShowController {    @Value(value = "${statementshow.ftpHost}")    String ftpHost;    @Value(value = "${statementshow.ftpUserName}")    String ftpUserName;    @Value(value = "${statementshow.ftpPort}")    int ftpPort;    @Value(value = "${statementshow.ftpPassword}")    String ftpPassword;    @Value(value = "${statementshow.hostDirectory}")    String hostDirectory;    @GetMapping("/downloadfile")    public DataResult<Boolean>  downloadFile (@RequestParam("fdate") String fdate, @RequestParam("fname") String fname,                              HttpServletRequest request, HttpServletResponse response){        if (StringUtils.isBlank(fdate) || StringUtils.isBlank(fname)) {            return new DataResult<>(403, "fdate,fileName不能为空.", false);        }        log.info("downloadfile--sftpInfo:"+ftpHost+","+ftpUserName+","+ftpPort+","+ftpPassword+","+hostDirectory+","+fdate+","+fname);        String filePath =hostDirectory+"/"+fdate;        byte[] buffer = new byte[1024 * 10];        InputStream fis = null;        SFTPUtil sftp = null;        OutputStream out = null;        try {            //sftp登录            log.info("sftplogin:begin");            sftp = new SFTPUtil(ftpUserName, ftpPassword, ftpHost,ftpPort);            sftp.login();            log.info("sftplogin:success");            //判断有无对应文件            if (!sftp.isExist(filePath, fname)) {                log.info("sftp:文件或路径未找到");                throw new ServiceException("文件未找到");            }            response.setContentType("application/force-download;charset=UTF-8");            final String userAgent = request.getHeader("USER-AGENT");            try {                String fileName = null;                if (org.apache.commons.lang3.StringUtils.contains(userAgent, "MSIE") || org.apache.commons.lang3.StringUtils.contains(userAgent, "Edge")) {                    // IE浏览器                    fileName = URLEncoder.encode(fname, "UTF8");                } else if (org.apache.commons.lang3.StringUtils.contains(userAgent, "Mozilla")) {                    // google,火狐浏览器                    fileName = new String(fname.getBytes(), "ISO8859-1");                } else {                    // 其他浏览器                    fileName = URLEncoder.encode(fname, "UTF8");                }                response.setHeader("Content-disposition", "attachment; filename=" + fileName);            } catch (UnsupportedEncodingException e) {                log.error(e.getMessage(), e);                return null;            }            fis = sftp.download(fname);            out = response.getOutputStream();            //读取文件流            int len = 0;            while ((len = fis.read(buffer)) != -1) {                out.write(buffer, 0, len);            }            return new DataResult<>(200, "文件成功下载.", true);        } catch (Exception e) {            e.printStackTrace();            log.error("文件下载失败",e);            return new DataResult<>(403, "文件下载失败.请检查文件路径", false);        } finally {            sftp.logout();            if (out != null) {                try {                    out.close();                } catch (IOException e) {                    out = null;                }            }            if (fis != null) {                try {                    fis.close();                } catch (IOException e) {                    fis = null;                }            }        }    }}
此时代码已经初步完毕,只要你的项目编码与sftp目标主机编码一致就完全没问题。
问题是我的项目编码是utf-8,目标主机用的是gbk编码。此时可以下载不带中文的文件。带中文名称的报找不到对应文件的错误、。
此时可以设置ChannelSftp的编码来解决问题,调用setFilenameEncoding("GBK")即可。
问题是调用这个方法以后项目运行到这一行报错了,报错信息是:The encoding can not be changed for this sftp server.
debug进这个方法:
发现他的version是3,而version在3与5之间是不可以设置编码的。
一开始我尝试换该jar包的版本,但是换了好几个发现这个version依旧是3.
此时只能通过反射来修改这个属性的值,进而使setFilenameEncoding("GBK")生效。
在login方法最后加以下代码:
           Class<?> cl = ChannelSftp.class;            Field f =cl.getDeclaredField("server_version");            f.setAccessible(true);            f.set(sftp, 2);            sftp.setFilenameEncoding("GBK");
OK~,成功下载。
有相同爱好的可以进来一起讨论哦:企鹅群号:1046795523


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-9 16:53

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表