关于FTP下载大文件程序卡住以及执行方法时的线程阻塞

文章介绍


本人新手第一次发博客,且经验也不是很丰富,可能还存在很多问题,希望有大佬能指导下,我这篇文章主要还是给个参考

话不多说,掏出我那长长的。。。

第一步:连接ftp服务器

连接这里要注意的是设置ftpclient超时属性一定要在connect(连接)前设置好,否则不会生效

其中阻塞问题是设置ftpClient.setSoTimeout(1 * 60 * 1000);这个属性是超时处理,且不是传输过程的总超时时间,而是每次的超时时间。

总之,setSoTimeout() 用于设置从 Socket 的输入流中读取数据时每次陷入阻塞过程的超时时间。

     /**
     * 初始化ftp服务器
     */
    public void initFtpClient() {
        ftpClient.setControlEncoding("utf-8");
        try {
            System.out.println("connecting...ftp服务器:" + this.hostname + ":" + this.port);

            ftpClient.setDefaultTimeout(10 * 60 * 1000);
            ftpClient.setConnectTimeout(10 * 60 * 1000);
            ftpClient.setDataTimeout(10 * 60 * 1000);

            ftpClient.connect(hostname, port); //连接ftp服务器
            //设置FTP属性
            ftpClient.enterLocalPassiveMode();//被动模式
            // socket连接,设置socket连接超时时间(单位:ms)
            ftpClient.setSoTimeout(1 * 60 * 1000);
            ftpClient.login(username, password); //登录ftp服务器
            ftpClient.setBufferSize(1024 * 1024 * 32);
            ftpClient.setControlKeepAliveTimeout(3000);
            ftpClient.setControlKeepAliveReplyTimeout(3000);
            //ftpClient.setKeepAlive(true);
            ftpClient.setRemoteVerificationEnabled(false);

            int replyCode = ftpClient.getReplyCode(); //是否成功登录服务器
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                System.out.println("connect failed失败...ftp服务器:" + this.hostname + ":" + this.port);
            } else {
                System.out.println("connect successful成功...ftp服务器:" + this.hostname + ":" + this.port);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

第二步:下载ftp文件至本地

为什么我在下载这里加连接ftp,而不在外部直接调用,就当你下载多个文件(不考虑说下载整个文件夹)时,我认为的是它每次连接之后用完,不是有个关闭连接吗,然后它就会一个在寻求连接,一直在哪等待,程序就不走了,还有就是设置被动模式ftpClient.enterLocalPassiveMode(); 这个的作用是针对于防火墙以及Linux系统部分(默认主动),因为它每次主动去拿,会被系统所拦截掉,然后需要等待它给。造成程序假死还有一个原因ftpClient.retrieveFile(ff.getName(), os);这个方法,你在连接的时候设置我上面的那些个属性就不会有问题,也不会卡死,还有就是因为ftp连接那个方法是开启一个线程的,然后你下载文件也是用同一个线程,然后就会阻塞,我这边只是设置了一些ftp属性,你也可以通过Thread类开另外一个线程来进行(与连接方法放开,单独跑一个)

    /**
     * 从FTP服务器下载文件
     * <p>
     * ftpPath   FTP服务器中文件所在路径
     *
     * @param remotePath FTP服务器上的相对路径
     * @param fileName   要下载的文件名
     * @Param localPath下载后保存到本地的路径
     */
    public static boolean downFile(String remotePath, String fileName, String localPath) {
        ftp.initFtpClient();
        boolean success = false;
        try {
            ftpClient.changeWorkingDirectory(remotePath);//转移到FTP服务器目录
            ftpClient.enterLocalPassiveMode();
            FTPFile[] fs = ftpClient.listFiles();
            for (FTPFile ff : fs) {
                if (ff.getName().equals(fileName)) {
                    File localFile = new File(localPath + "/" + ff.getName());
                    OutputStream os = new FileOutputStream(localFile);
                    ftpClient.retrieveFile(ff.getName(), os);
                    os.close();
                    success = true;
                }
            }
            ftpClient.logout();
        } catch (IOException e) {
            System.err.println("【防止阻塞】IOE错误  程序继续。。。");
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException ioe) {
                }
            }
        }
        return success;
    }

第三步:测试 

FTPFile[] ftplist = ftp.ftpFileList();
        List<File> filelist = ftp.localFileList();
        try {
            for (int j = 0; j < ftplist.length; j++) {
                boolean flag = true;
                for (int i = 0; i < filelist.size(); i++) {
                    if (ftplist[j].getName().equals(filelist.get(i).getName())) {
                        if (ftplist[j].getSize() == filelist.get(i).length()) {
                            flag = false;
                        }
                    }
                }
                if (flag) {
                   booleanb=downFile("/gxgplay",ftplist[j].getName(),"D:\\ftpTest");
                    if (b) {
                        System.out.println("成功完成文件下载:" + ftplist[j].getName());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

总结:

  • setConnectTimeout() 用于设置终端 Socket 与 FTP 服务器建立连接这个过程的超时时间。
  • setDefaultTimeout() 用于设置终端的传输控制命令的 Socket 的 SoTimeout,即针对传输控制命令的 Socket 的输入流做读取操作时每次陷入阻塞的超时时间。
  • setSoTimeout() 作用跟上个方法一样,区别仅在于该方法设置的超时会覆盖掉上个方法设置的值。
  • setDataTimeout() 用于设置终端的传输数据的 Socket 的 Sotimeout,即针对传输文件数据的 Socket 的输入流做读取操作时每次陷入阻塞的超时时间。
  • setControlKeepAliveTimeout() 用于设置当处于传输数据过程中,按指定的时间阈值定期让传输控制命令的 Socket 发送一个无操作命令 NOOP 给服务器,让它 keep alive。
  • setControlKeepAliveReplyTimeout():只有调用上个方法后,该方法才能生效,用于设置在传输数据这个过程中,暂时替换掉传输控制命令的 Socket 的 SoTimeout,传输过程结束恢复这个 Socket 原本的 SoTimeout。

 

               以上是我的个人理解,修为甚浅,还请见谅,最后这个是无意看到的,希望对你有帮助!✈

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值