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

    最近接了一个文件下载接口需求,需要采用sftp形式与对端主机连接,进行文件传输。

    首先引入java操作sftp的工具类包:

        <dependency>
    	<groupId>com.jcraft</groupId>
    	<artifactId>jsch</artifactId>
    	<version>0.1.53</version>
         </dependency>
    

    编写文件工具类(包含sftp连接、断开、下载文件等方法)

    /**
     * sftp形式下载文件
     *
     * @author wangshuai
     *
     */
    @Slf4j
    public 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")
    @Slf4j
    public 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~,成功下载。

  • 相关阅读:
    鼠标不灵了,还好只是线的问题。自己DIY修下了
    [摘]编译MPlayer
    TPLINK路由器 硬重启方法
    Visual C++线程同步技术剖析 (转载)
    CListCtrl一行显示多个图标问题
    一位软件工程师的6年总结
    CCIE红头发讲解CCNA、CCNP视频教程
    图片链
    [摘]如何级联两个TPLINK路由器
    [摘]测试一下你对IP地址的掌握水平
  • 原文地址:https://www.cnblogs.com/keyforknowledge/p/13565756.html
Copyright © 2011-2022 走看看