【问题标题】:Downloading an XML file via FTP通过 FTP 下载 XML 文件
【发布时间】:2012-08-18 05:04:17
【问题描述】:

我在数据库中有一个提要列表,我用它从 FTP 服务器下载 XML 文件,然后对其进行解析。该脚本被捆绑到一个 jar 文件中,该文件每天使用 Windows 任务计划程序运行。有时,请求会在抓取某个 xml 文件时被拖走。到目前为止,它在 2 周内发生了大约 3 次,我看不到真正的模式。

当它出现问题时,我转到运行它的计算机,我看到命令窗口打开,并且在 xml 完全下载之前停止。如果我关闭命令窗口并手动运行任务,一切都会正常工作。

我用来下载 xml 文件的代码是:

private void loadFTPFile(String host, String username, String password, String filename, String localFilename){
        System.out.println(localFilename);
        FTPClient client = new FTPClient();
        FileOutputStream fos = null;

        try {
            client.connect(host);
            client.login(username, password);
            String localFilenameOutput = createFile(assetsPath + localFilename);
            fos = new FileOutputStream(localFilenameOutput);
            client.retrieveFile(filename, fos);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) 
                    fos.close();
                client.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

这个函数被循环调用,当它失败时,一切都会停止,脚本不会进入下一个提要。

我不确定发生了什么,可能连接丢失,但我认为如果发生这种情况,try/catch 会捕获。我不确定超时是否可以解决问题或需要使用线程(但我从未使用过线程)

谁能指出我正确的方向为什么会发生这种情况以及我可以做些什么来解决这个问题

【问题讨论】:

  • 程序挂起时,最后一个文件已经下载成功了吗?
  • 不,它只是文件的药水
  • 因此您可以尝试在数据连接上设置超时以防止服务器故障 - 请参阅我的更新答案

标签: java ftp windows-task-scheduler


【解决方案1】:

这不是线程问题。很可能它是由循环中的某些东西引起的,因为该代码看起来应该清理得很好。也就是说,为了进行测试,您可能需要添加

catch (Exception e) {
    e.printStackTrace();
}

IOException catch 子句之后。可能正在引发另一个异常。

另一件事,如果您一次从数据库结果集中提取一个结果并进行 FTP 获取,这可能是个问题。除非结果都被 JDBC 调用一次带回,否则也可能超时。并非所有数据库查询实际上都会一次将整个结果集返回给客户端。

【讨论】:

    【解决方案2】:

    您是否检查任何调用的返回状态或者是代码?

    有一个调用 completePendingCommand() 必须在某些情况下使用。这可能是需要调查的事情。

    另外,你不会看到 IO 异常,我相信它会被重新打包为 CopyStreamException

    您可能还想将返回值更改为布尔值,因为您捕获了异常,至少调用循环会知道转移是否发生。

    private boolean loadFTPFile(String host, String username, String password, String filename, String localFilename){
        System.out.println(localFilename);
        FTPClient client = new FTPClient();
        FileOutputStream fos = null;
    
        try {
            client.connect(host);
    
            int reply = client.getReplyCode();
    
            if (!FTPReply.isPositiveCompletion(reply)){
                client.disconnect();
                System.err.println("FTP server refused connection.");
                return false;
            }
    
    
            if (!client.login(username, password)){
                client.logout();
                return false;
            }
    
            String localFilenameOutput = createFile(assetsPath + localFilename);
            fos = new FileOutputStream(localFilenameOutput);
            boolean result = client.retrieveFile(filename, fos);
    
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    
            if (result){
                System.out.println("\tFile Transfer Completed Successfully at: " + sdf.format(Calendar.getInstance().getTime()));
    
                // ftp.completePendingCommand();
            }
            else {
                System.out.println("\tFile Transfer Failed at: " + sdf.format(Calendar.getInstance().getTime()));
            }
    
        return result;
        }catch (CopyStreamException cse){
            System.err.println("\n\tFile Transfer Failed at: " + sdf.format(Calendar.getInstance().getTime()));
            System.err.println("Error Occurred Retrieving File from Remote System, aborting...\n");
            cse.printStackTrace(System.err);
            System.err.println("\n\nIOException Stack Trace that Caused the Error:\n");
            cse.getIOException().printStackTrace(System.err);
            return false;
        }catch (Exception e){
            System.err.println("\tFile Transfer Failed at: " + sdf.format(Calendar.getInstance().getTime()));
            System.out.println("Error Occurred Retrieving File from Remote System, aborting...");
            e.printStackTrace(System.err);
            return false;
        } finally {
            try {
                if (fos != null) 
                    fos.close();
                client.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      更新 - 设置数据连接超时

      由于最后一个文件只下载了部分,并且给出了FTPClient.retrieveFile()的来源,我认为这可能是服务器端的问题(导致它挂起,甚至死亡的东西 - 谁知道 em>)。显然无法修复服务器,甚至无法知道那里发生了什么,无论如何我建议使用setDataTimeout(int) 添加超时并分别捕获可能的SocketTimeoutException 以登录到不同的位置并可能发送给FTP服务器管理员(连同它发生的时间信息),以便他们可以合并日志并查看问题所在。

      老答案

      我没有注意到你为每个文件都连接和登录,所以下面只是一个优化不要关闭控制连接并成功注销,但它应该 解决问题。

      您可以在调试模式下启动 JVM 并在它挂起时附加一个调试器,无论如何根据this answerthis thread 这可能是网络设备设备(路由器)上的超时问题。来自FTPClient Javadoc

      在文件传输过程中,数据连接繁忙,但控制 连接空闲。 FTP 服务器知道控制连接在 使用,因此不会因缺乏活动而关闭它,但这要困难得多 让网络路由器知道控制和数据连接是 相互关联。一些路由器可能会将控制 连接为空闲,如果通过数据传输则断开连接 连接所需的时间超过了路由器允许的空闲时间。

      对此的一种解决方案是通过控制连接发送安全命令(即 NOOP)以重置路由器的空闲计时器。如下启用:

      ftpClient.setControlKeepAliveTimeout(300); // set timeout to 5 minutes
      

      【讨论】:

      • 关于文件大小的要点很好,但如果您在防火墙或大型机环境中,连接设置也非常关键,因为它们会在很短的时间内杀死任何不活动的连接。
      • @Mike 不错的补充 :) 无论如何,即使在这样的环境中,我认为程序所能做的就是继续发送 NOOP。还是有一些特殊的连接配置?
      • 网络公地并不总是抛出异常。对于大文件, setControlKeepAliveTimeout(300) 应该阻止它消失。但这不应该是这里的问题,因为他每次都通过循环建立一个新连接(该方法是自包含的)。他也没有切换到被动模式 enterLocalPassiveMode(),所以他通过控制连接发送文件。我认为它运行得如此之快,服务器可能有问题并拒绝连接。代码有几个关键点他没有检查返回值。
      • 他通过控制连接发送文件是什么意思?我是 FTP 新手,但我认为文件传输总是发生在专用连接中 - 此外,如果服务器拒绝连接,他会遇到异常,但实际上程序只是挂起。你认为程序在哪里永远等待? source of retrieveFile()
      • 你说得对,我有一瞬间失去了理智……或两……我不确定他在哪里挂断了电话,但在服务器繁忙的情况下,你会得到否定的答复来自服务器,因为它不是套接字错误,所以不会出现异常。
      猜你喜欢
      • 1970-01-01
      • 2020-05-25
      • 1970-01-01
      • 2012-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多