在Java中如何查找大文件复制状态

3

我有一个要求,需要在Java中监视目录中文件的复制状态,并且文件会不断地放入该目录。 我计划使用Executor框架来查找单个文件的复制状态,并编写了下面的代码,但它的工作效果并不如预期,未完成复制的文件却提示复制已完成。

private boolean isFileCopied(String filePath) {
    File file = new File(filePath);

    Scanner scanner;
    boolean isCopied = true;
    while (true) {
        try {
            scanner = new Scanner(file);
            isCopied = false;
        } catch (FileNotFoundException e) {
            System.out.println(filePath + " File is in copy State. ");
            sleepFile();
        }
        if (isCopied == false) {
            break;
        }
    }
    System.out.println(filePath + "  copy completed");
    return isCopied;
}

   private static void sleepFile() {
    System.out.println("sleeping for 10 seconds");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

请有人帮我找到文件的确切状态,例如“复制进行中”或“复制完成”,并且如何监视每个文件的复制状态,如果有一堆大文件放在一个目录中。

我已经使用了Watcher API,但它没有解决我的问题。即使没有复制的文件也完成了,我也会得到复制完成的通知。以下是我的代码更改。

使用观察程序服务:

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class FolderWatchDemo {
public static void main(String[] args) {

    final Path outputWatchFolderPath =  Paths.get("/outputFolder/");
    final Path sourceFolderPath = Paths.get("/sourceFolder/");

    try {
        //Registering outputWatchFolderPath
        WatchService watcher = FileSystems.getDefault().newWatchService();
        Path dir = Paths.get(outputWatchFolderPath.toString());

        dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        System.out.println("Watch Service registered for dir: " + dir.getFileName());

        //copy files from inputfolder to o
        for (final Path path: Files.newDirectoryStream(sourceFolderPath))
            Files.copy(path, outputWatchFolderPath.resolve(path.getFileName()));

        while (true) {
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException ex) {
                return;
            }

            for (WatchEvent<?> event : key.pollEvents()) {
                 WatchEvent.Kind<?> kind = event.kind();

                WatchEvent<Path> ev = (WatchEvent<Path>) event;
                if (kind == ENTRY_CREATE) {
                    System.out.println("file got created !!");
                }
                if (kind == ENTRY_MODIFY) {
                    System.out.println("copying got completed !!");
                }
                if (kind == ENTRY_DELETE) {
                    System.out.println("file deleted successfully !!");
                }
            }
            boolean valid = key.reset();
            if (!valid) {
                break;
            }
        }

    } catch (IOException ex) {
        System.err.println(ex);
    }
}
}

提前感谢你。


1
你为什么要创建一个“Scanner”?为什么不直接检查“file.exists()”呢? - Andy Turner
2
还有,为什么要轮询文件,而不使用WatchService呢? - Andy Turner
我也使用了监视服务,即使文件还没有复制,我仍然收到了完成的通知。实际上,使用监视服务时我并没有得到准确的结果。 - Raaz
也许你可以先尝试获取原始文件大小,然后计算复制文件大小并进行比较。 - andy
请展示你使用 WatchService 的代码。 - Andy Turner
嗨,安迪,我已经在第一篇帖子中添加了监视服务API代码片段。请查看并告诉我是否有遗漏的地方。 - Raaz
2个回答

4
有一种非常优雅的方法可以创建一个回调系统,并通过实现ReadableByteChannel来实现它,以监视文件复制的进度。 另外的好处是不需要监视目录。 您可以明确地监视正在复制的文件的进度。
这个主要想法是由这个网站提出的,但稍微改变了一下以适应您的问题:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Paths;

interface ProgressCallBack {
    public void callback(CallbackByteChannel rbc, double progress);
}   

public class Main{
    public static void main(String[] args) {

        ProgressCallBack progressCallBack = new ProgressCallBack() {
            @Override
            public void callback(CallbackByteChannel rbc, double progress) {
                System.out.println(rbc.getReadSoFar());
                System.out.println(progress);
            }
        };

        try {
            copy("SOURCE FILE PATH", "DESTINATION FILE PATH", progressCallBack);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void copy(String source, String destination, ProgressCallBack callBack) throws IOException {
        FileOutputStream fos = null;
        FileChannel sourceChannel = null;
        try {
            sourceChannel = new FileInputStream(new File(source)).getChannel();
            ReadableByteChannel rbc = new CallbackByteChannel(sourceChannel, Files.size(Paths.get(source)), callBack);
            fos = new FileOutputStream(destination);
            fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(sourceChannel.isOpen()){
                sourceChannel.close();
            }
            fos.close();
        }
    }
}

class CallbackByteChannel implements ReadableByteChannel {
    ProgressCallBack delegate;
    long size;
    ReadableByteChannel rbc;
    long sizeRead;

    CallbackByteChannel(ReadableByteChannel rbc, long expectedSize, ProgressCallBack delegate) {
        this.delegate = delegate;
        this.size = expectedSize;
        this.rbc = rbc;
    }

    public void close() throws IOException {
        rbc.close();
    }

    public long getReadSoFar() {
        return sizeRead;
    }

    public boolean isOpen() {
        return rbc.isOpen();
    }

    public int read(ByteBuffer bb) throws IOException {
        int n;
        double progress;
        if ((n = rbc.read(bb)) > 0) {
            sizeRead += n;
            progress = size > 0 ? (double) sizeRead / (double) size * 100.0 : -1.0;
            delegate.callback(this, progress);
        }
        return n;
    }
}

希望这能帮助到您。

非常感谢STaefi,它起作用了...但是对于我的要求,一些外部工具将复制文件到目标文件夹..我只需要观察输出文件夹中的文件是否已经复制完成.. ProgressCallBack progressCallBack = new ProgressCallBack() { @Override public int callback(CallbackByteChannel rbc, double progress) { return (int) progress; } }; - Raaz
如果进度达到100,那么我就认为它已经复制完成了。但是,如果复制是由某个外部工具完成的,我该如何实现相同的状态检测呢? - Raaz
你有哪些外部工具?那么您关于监视复制单个文件的要求现在是否已更改?您需要监视一个目录吗? - STaefi
是的,STaefi,我需要监控目录,实际上外部Web应用程序会不断地将大型文件复制到我们配置的目录中,我们需要找到每个文件的复制状态,如果复制成功,则需要为另一个业务需求处理该文件。 - Raaz
起初,您的要求也让我想到了监视(轮询)目标目录更改的想法。之后我问自己为什么要监视那个目录?然后我考虑了一下,想出了一个在每次单独完成复制后触发某些事件的系统。因此,您可以在每次复制达到100%时调用一种方法。您在您的ProgressCallBack实现中拥有此事件(达到100%)。因此,您甚至可以将已完成文件的名称传递给该方法并在其中使用它。 - STaefi
显示剩余8条评论

0
package test;

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class FolderWatchDemo {
    public static void main(String[] args) {

        final Path outputWatchFolderPath =  Paths.get("/outputFolder/");
        final Path sourceFolderPath = Paths.get("/sourceFolder/");

        try {
            // Registering outputWatchFolderPath
            WatchService watcher = FileSystems.getDefault().newWatchService();
            Path dir = Paths.get(outputWatchFolderPath.toString());

            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
            System.out.println("Watch Service registered for dir: " + dir.getFileName());

                    // copy files from inputfolder to o
                        for (final Path path : Files.newDirectoryStream(sourceFolderPath))
                            Files.copy(path, outputWatchFolderPath.resolve(path.getFileName()),
                                    StandardCopyOption.REPLACE_EXISTING);

            String fileName = null;
            while (true) {
                WatchKey key;
                try {
                    key = watcher.take();
                } catch (InterruptedException ex) {
                    return;
                }

                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();

                    WatchEvent<Path> ev = (WatchEvent<Path>) event;
                    if (kind == ENTRY_CREATE) {
                        System.out.println("file got created !!" + ev.context());
                    }
                    if (kind == ENTRY_MODIFY) {
                        String fName = ev.context().getFileName().toString();
                        if (fileName != null && !fName.equals(fileName)) {
                            System.out.println("file copying completed !!" + fileName);
                        }
                        fileName = fName;
                        System.out.println("copying " + fName);
                    }
                    if (kind == ENTRY_DELETE) {
                        System.out.println("file deleted successfully !!");
                    }
                }
                boolean valid = key.reset();
                if (!valid) {
                    break;
                }
            }

        } catch (IOException ex) {
            System.err.println(ex);
        }
    }
}

只有在目标文件夹中只有一个复制线程且没有其他修改时才有效。 - andy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接