关于 java:如何在 Apache Commons VFS 中监控文件传输的进度?

How do I monitor the progress of a file transfer in Apache Commons VFS?

使用 Apache Commons VFS,我如何监控文件传输的进度。我需要能够通过上传和下载来做到这一点。我还需要监控 HTTP、FTP、SFTP 和 FTPS 的进度。我在文档中找不到任何关于它的内容。


这可以通过从 VFS 获取输入和输出流来完成。以下示例使用来自 commons-net(VFS 的依赖项)的实用程序类来管理复制和进度监控。您同样可以手动完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.apache.commons.net.io.Util;
import org.apache.commons.net.io.CopyStreamListener;

private void copy(FileObject sourceFile, FileObject destinationFile, CopyStreamListener progressMonitor) throws IOException {
    InputStream sourceFileIn = sourceFile.getContent().getInputStream();
    try {
        OutputStream destinationFileOut = destinationFile.getContent().getOutputStream();
        try {
            Util.copyStream(sourceFileIn, destinationFileOut, Util.DEFAULT_COPY_BUFFER_SIZE, sourceFile.getContent().getSize(), progressMonitor);
        } finally {
            destinationFileOut.close();
        }
    } finally {
        sourceFileIn.close();
    }
}


建议的解决方案有点过时,而且有点"弱"。让我们尝试一个更整洁的!
注意:以下代码需要 JDK 16.

给出一个使用标准启动的示例复制操作:

1
2
3
4
final var manager = VFS.getManager();
final var origin = manager.resolveFile(originUri.toString(), fileSystemOptions);
final var destination = manager.resolveFile(destinationUri.toString(), fileSystemOptions);
destination.copyFrom(origin, Selectors.SELECT_ALL);

使用自定义委托实现简单地package目标 FileObject,我们可以称为 ProgressFileObject,它也将接受 ProgressListener:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
final var listener = new ProgressListener() {
  @Override
  public void started() {
    System.out.println("Started");
  }

  @Override
  public void completed() {
    System.out.println("Completed");
  }

  @Override
  public void failed(@NotNull final Exception e) {
    System.out.println("Failed:" + e.getMessage());
  }

  @Override
  public void progress(@NotNull final ProgressEvent event) {
    final var out ="%d\\t\\t\\t%d\\t\\t\\tFile: %s".formatted(
        event.totalBytes(),
        event.totalTransferredBytes(),
        event.lastFileDestinationPath()
    );

    System.out.println(out);
  }
};

final var progressDestination = new ProgressFileObject(destination, listener);
progressFileObject.copyFrom(origin, Selectors.SELECT_ALL);

这将导致一个文件夹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Started
4648320         119391          File: /test_dir/AN/ANI0.evt
4648320         119511          File: /test_dir/AN/ANI0.hpj
4648320         119584          File: /test_dir/AN/ANI0.ipf
4648320         119585          File: /test_dir/AN/ANI0.ipm
4648320         1907060         File: /test_dir/AN/ANI0.LST
4648320         1907253         File: /test_dir/AN/ANI0.MRG
4648320         2472700         File: /test_dir/AN/ANI0.ODF
4648320         2472707         File: /test_dir/AN/ANI0.ph
4648320         2473421         File: /test_dir/AN/ANI0.rbj
4648320         2473547         File: /test_dir/AN/ANI0.rc
4648320         2473708         File: /test_dir/AN/ANI0.rst
4648320         2474813         File: /test_dir/AN/ANI0.rtf
4648320         2474814         File: /test_dir/AN/ANI0.txc
4648320         2474819         File: /test_dir/AN/ANI0.txm
4648320         2474820         File: /test_dir/AN/ANI0.vpf
4648320         2829348         File: /test_dir/AN/ANI0.VPG
4648320         2829466         File: /test_dir/AN/P0000001.rc
4648320         2829592         File: /test_dir/AN/P0000002.rc
4648320         4648275         File: /test_dir/AN/VPGSAV55.C2T
4648320         4648320         File: /test_dir/AN/VRPGWIN.RC
Completed

以下是 ProgressFileObjectProgressListenerProgressEvent 来源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
 * @author Edoardo Luppi
 */

public class ProgressFileObject extends DecoratedFileObject {
  private final FileObject fileObject;
  private final ProgressListener listener;

  public ProgressFileObject(
      @NotNull final FileObject fileObject,
      @NotNull final ProgressListener listener) {
    super(fileObject);
    this.fileObject = fileObject;
    this.listener = listener;
  }

  @Override
  public void copyFrom(final FileObject file, final FileSelector selector) throws FileSystemException {
    if (!FileObjectUtils.exists(file)) {
      final var exception = new FileSystemException("vfs.provider/copy-missing-file.error", file);
      listener.failed(exception);
      throw exception;
    }

    listener.started();

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

    // Calculate the total bytes that will be copied
    var totalBytes = 0L;

    for (final var srcFile : files) {
      if (srcFile.getType().hasContent()) {
        totalBytes += srcFile.getContent().getSize();
      }
    }

    var totalTransferredBytes = 0L;

    // Copy everything across
    for (final var srcFile : files) {
      // Determine the destination file
      final var relPath = file.getName().getRelativeName(srcFile.getName());
      final var destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);

      // Clean up the destination file, if necessary
      if (FileObjectUtils.exists(destFile) && destFile.getType() != srcFile.getType()) {
        // The destination file exists, and is not of the same type, so delete it
        destFile.deleteAll();
      }

      // Copy across
      try {
        if (srcFile.getType().hasContent()) {
          try (final var content = srcFile.getContent()) {
            final var fileTransferredBytes = content.write(destFile);
            totalTransferredBytes += fileTransferredBytes;
            final var event = new ProgressEvent(
                totalBytes,
                totalTransferredBytes,
                fileTransferredBytes,
                srcFile.getName().getPath(),
                destFile.getName().getPath()
            );
       
            listener.progress(event);
          }
        } else if (srcFile.getType().hasChildren()) {
          destFile.createFolder();
        }
      } catch (final IOException e) {
        final var exception = new FileSystemException("vfs.provider/copy-file.error", e, srcFile, destFile);
        listener.failed(exception);
        throw exception;
      }
    }

    listener.completed();
  }

  @Override
  public URI getURI() {
    return fileObject.getURI();
  }

  @Override
  public Path getPath() {
    return fileObject.getPath();
  }

  @Override
  public boolean isSymbolicLink() throws FileSystemException {
    return fileObject.isSymbolicLink();
  }

  @Override
  public void forEach(final Consumer<? super FileObject> action) {
    fileObject.forEach(action);
  }

  @Override
  public Spliterator<FileObject> spliterator() {
    return fileObject.spliterator();
  }

  @Override
  public int hashCode() {
    return fileObject.hashCode();
  }

  @Override
  public boolean equals(final Object obj) {
    return fileObject.equals(obj);
  }

  @Override
  public String toString() {
    return fileObject.toString();
  }

  public interface ProgressListener {
    void started();
    void completed();
    void failed(@NotNull final Exception e);
    void progress(@NotNull final ProgressEvent event);
  }

  public record ProgressEvent(
      long totalBytes,
      long totalTransferredBytes,
      long lastFileTransferredBytes,
      String lastFileOriginPath,
      String lastFileDestinationPath
  ) {}
}