【问题标题】:Copy from SFTP interrupted using Apache Commons VFS使用 Apache Commons VFS 从 SFTP 复制中断
【发布时间】:2017-11-19 23:30:15
【问题描述】:

我正在尝试使用 Apache Commons VFS 实用程序从 SFTP 服务器复制文件。

在复制时,如果出现任何类型的网络故障,预计我会收到IOException。但我找不到任何抛出的异常(检查了我的日志)。因此,我找到了一个被复制了一半的文件。 (尝试使用文本文件。)下面是代码 sn-p:

public class SFTPFileHandler implements IFileSystemHandler {

    private String hostName;
    private String userName;
    private String password;
    private String knownHost;
    private String privateKey;
    private FileSystemOptions fileSystemOptions;
    private StandardFileSystemManager fileSystemManager;
    private FileObject remoteRootDirectory;
    private boolean initialized = false;
    private FileType fileType;

    //code to initialize stuff
    ....

    /**
     * Method to Connect to the Server
     * 
     * @throws URISyntaxException
     * @throws FileSystemException
     * @throws FileHandlerInitializationException
     */
    private void connect() throws URISyntaxException, FileSystemException, FileHandlerInitializationException {
        createDefaultOptions();
        String connectionUrl = buildConnectionUrl();
        remoteRootDirectory = fileSystemManager.resolveFile(connectionUrl,fileSystemOptions);
    }



    /**
     * Method to copy a from the local file system to SFTP server
     */
    public void localToRemoteCopy(String srcPath, String destPath) throws FileSystemException {
        LocalFile localFileObject = null;
        FileObject remoteFileObject = null;
        try {
            localFileObject = (LocalFile) fileSystemManager
                    .resolveFile(srcPath);
            remoteFileObject = remoteRootDirectory.resolveFile(destPath);
            remoteFileObject.copyFrom(localFileObject, new AllFileSelector());
        } finally {
            if(null != localFileObject ){
            localFileObject.close();
            }
            if(null != remoteFileObject ){
            remoteFileObject.close();
            }
        }
    }

    // other code

}

如果我查看源代码,它确实会引发异常。

/**
     * Copies another file to this file.
     * @param file The FileObject to copy.
     * @param selector The FileSelector.
     * @throws FileSystemException if an error occurs.
     */
    public void copyFrom(final FileObject file, final FileSelector selector)
        throws FileSystemException
    {
        if (!file.exists())
        {
            throw new FileSystemException("vfs.provider/copy-missing-file.error", file);
        }
        /* we do not alway know if a file is writeable
        if (!isWriteable())
        {
            throw new FileSystemException("vfs.provider/copy-read-only.error", new Object[]{file.getType(),
            file.getName(), this}, null);
        }
        */

        // Locate the files to copy across
        final ArrayList<FileObject> files = new ArrayList<FileObject>();
        file.findFiles(selector, false, files);

        // Copy everything across
        final int count = files.size();
        for (int i = 0; i < count; i++)
        {
            final FileObject srcFile = files.get(i);

            // Determine the destination file
            final String relPath = file.getName().getRelativeName(srcFile.getName());
            final FileObject destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);

            // Clean up the destination file, if necessary
            if (destFile.exists() && destFile.getType() != srcFile.getType())
            {
                // The destination file exists, and is not of the same type,
                // so delete it
                // TODO - add a pluggable policy for deleting and overwriting existing files
                destFile.delete(Selectors.SELECT_ALL);
            }

            // Copy across
            try
            {
                if (srcFile.getType().hasContent())
                {
                    FileUtil.copyContent(srcFile, destFile);
                }
                else if (srcFile.getType().hasChildren())
                {
                    destFile.createFolder();
                }
            }
            catch (final IOException e)
            {
                throw new FileSystemException("vfs.provider/copy-file.error", new Object[]{srcFile, destFile}, e);
            }
        }
    }

我正在使用以下依赖项:

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-vfs2</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.53</version>
        </dependency>

【问题讨论】:

  • 查看您的代码,我发现了一些问题,try-catch 没有任何 catch 子句。您声明的方法正在抛出异常,但它们在哪里捕获?
  • 我在我的客户端代码中捕捉到它们。
  • 在特定文件上是否系统化?还是时不时会发生这种情况?或者这是一次偶然的事故?
  • 没有。每当(目标和 SFTP)服务器由于网络问题而失去连接时,就会发生这种情况。我的客户端代码中没有发现异常。这就是问题所在。我得到了一个复制了一半的文件。
  • 嗯,奇怪,我昨天试过了:用 commons-vfs 开始一个 ssh 到本地的文件复制(试过 2.0 和 2.1,用 jsch 0.1.53 和 0.1.54),禁用 wifi中间,我得到一个部分复制的文件和一个堆栈跟踪。你对你的日志配置有信心吗?也许尝试 2.1,我认为日志记录更健谈。

标签: java apache-commons-vfs


【解决方案1】:

我正在考虑通过手动解决方法解决此问题,方法是对源文件和目标文件进行校验和匹配,然后在校验和不同时抛出异常。

【讨论】:

    【解决方案2】:

    我认为你可能是一个典型的例子,你最终掩盖了真正的例外。我怀疑如果您断开网络连接,那么您的 finally 块中的清理问题也会出现问题。您可以尝试修改 finally 块,以便在 finally 块中发生异常时实际捕获异常吗?

    改变

    } finally {
            if(null != localFileObject ){
            localFileObject.close();
            }
            if(null != remoteFileObject ){
            remoteFileObject.close();
            }
    }
    

    } finally {
            if(null != localFileObject ){
              try { 
                localFileObject.close();
              } catch (Exception ex) { //ignore or add just a logging call }
            }
            if(null != remoteFileObject ){
              try {
               remoteFileObject.close();
              } catch (Exception ex) { //ignore or add just a logging call }
            }
    }
    

    请记住,finally 块中引发的异常将隐藏主块中引发的异常。

    【讨论】:

    • 我已经从我的方法 localToRemoteCopy 中抛出 FileSystemException。在方法中过早捕获异常会导致我的客户端代码出现故障。如果 API 代码抛出异常,那么我的客户端代码肯定会捕获它。这是 API 没有抛出一个复制一半文件的异常的情况。
    • 看起来 VFS 有一个记录问题,你是对的,当使用 FileUtil.copyContent issues.apache.org/jira/browse/VFS-446 时,API 在网络中断期间不会引发异常。您还可以在票证中看到发起者转而使用 Java 期货及其自己的循环结构。
    猜你喜欢
    • 2015-08-03
    • 2014-02-19
    • 1970-01-01
    • 2017-01-19
    • 1970-01-01
    • 2020-08-06
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多