强制停止在外部线程上运行的 Java Files.copy()

2024-03-13

这里的答案似乎是 Java 8 之前的有效解决方案:如何在Java中取消Files.copy()? https://stackoverflow.com/q/17083896

但现在不行了,因为ExtendedCopyOption.INTERRUPTIBLE是私人的。


基本上,我需要从某个给定的地方下载文件URL并将其保存到我的本地文件系统中使用Files.copy()。 目前,我正在使用 JavaFX 服务,因为我需要显示进度ProgressBar.

但是我不知道如何阻止线程运行Files.copy()如果操作时间太长。 使用Thread.stop()至少是不想要的。甚至Thread.interrupt() fails.

如果互联网连接不可用,我还希望该操作能够正常终止。

为了测试没有互联网连接时的情况,我拔下了以太网线,并在 3 秒后将其放回原处。 很遗憾,Files.copy()仅当我放回以太网电缆时才返回,而我希望它立即失败。

据我所见,内部Files.copy()正在运行一个循环,这会阻止线程退出。


Tester(下载OBS Studio exe):

/**
 * @author GOXR3PLUS
 *
 */
public class TestDownloader extends Application {

    /**
     * @param args
     */
    public static void main(String[] args) {
    launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
    // Block From exiting
    Platform.setImplicitExit(false);

    // Try to download the File from URL
    new DownloadService().startDownload(
        "https://github.com/jp9000/obs-studio/releases/download/17.0.2/OBS-Studio-17.0.2-Small-Installer.exe",
        System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "OBS-Studio-17.0.2-Small-Installer.exe");

    }

}

DownloadService:

使用@sillyfly评论与FileChannel并删除File.copy似乎只适用于打电话Thread.interrupt()但当互联网不可用时它不会退出..

import java.io.File;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.concurrent.Service;
import javafx.concurrent.Task;

/**
 * JavaFX Service which is Capable of Downloading Files from the Internet to the
 * LocalHost
 * 
 * @author GOXR3PLUS
 *
 */
public class DownloadService extends Service<Boolean> {

    // -----
    private long totalBytes;
    private boolean succeeded = false;
    private volatile boolean stopThread;

    // CopyThread
    private Thread copyThread = null;

    // ----
    private String urlString;
    private String destination;

    /**
     * The logger of the class
     */
    private static final Logger LOGGER = Logger.getLogger(DownloadService.class.getName());

    /**
     * Constructor
     */
    public DownloadService() {
    setOnFailed(f -> System.out.println("Failed with value: " + super.getValue()+" , Copy Thread is Alive? "+copyThread.isAlive()));
    setOnSucceeded(s -> System.out.println("Succeeded with value: " + super.getValue()+" , Copy Thread is Alive? "+copyThread.isAlive()));
    setOnCancelled(c -> System.out.println("Succeeded with value: " + super.getValue()+" , Copy Thread is Alive? "+copyThread.isAlive()));
    }

    /**
     * Start the Download Service
     * 
     * @param urlString
     *            The source File URL
     * @param destination
     *            The destination File
     */
    public void startDownload(String urlString, String destination) {
    if (!super.isRunning()) {
        this.urlString = urlString;
        this.destination = destination;
        totalBytes = 0;
        restart();
    }
    }

    @Override
    protected Task<Boolean> createTask() {
    return new Task<Boolean>() {
        @Override
        protected Boolean call() throws Exception {

        // Succeeded boolean
        succeeded = true;

        // URL and LocalFile
        URL urlFile = new URL(java.net.URLDecoder.decode(urlString, "UTF-8"));
        File destinationFile = new File(destination);

        try {
            // Open the connection and get totalBytes
            URLConnection connection = urlFile.openConnection();
            totalBytes = Long.parseLong(connection.getHeaderField("Content-Length"));





            // --------------------- Copy the File to External Thread-----------
            copyThread = new Thread(() -> {

            // Start File Copy
            try (FileChannel zip = FileChannel.open(destinationFile.toPath(), StandardOpenOption.CREATE,
                StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {

                zip.transferFrom(Channels.newChannel(connection.getInputStream()), 0, Long.MAX_VALUE);


                // Files.copy(dl.openStream(), fl.toPath(),StandardCopyOption.REPLACE_EXISTING)

            } catch (Exception ex) {
                stopThread = true;
                LOGGER.log(Level.WARNING, "DownloadService failed", ex);
            }

            System.out.println("Copy Thread exited...");
            });
            // Set to Daemon
            copyThread.setDaemon(true);
            // Start the Thread
            copyThread.start();
            // -------------------- End of Copy the File to External Thread-------






            // ---------------------------Check the %100 Progress--------------------
            long outPutFileLength;
            long previousLength = 0;
            int failCounter = 0;
            // While Loop
            while ((outPutFileLength = destinationFile.length()) < totalBytes && !stopThread) {

            // Check the previous length
            if (previousLength != outPutFileLength) {
                previousLength = outPutFileLength;
                failCounter = 0;
            } else
                ++failCounter;

            // 2 Seconds passed without response
            if (failCounter == 40 || stopThread)
                break;

            // Update Progress
            super.updateProgress((outPutFileLength * 100) / totalBytes, 100);
            System.out.println("Current Bytes:" + outPutFileLength + " ,|, TotalBytes:" + totalBytes
                + " ,|, Current Progress: " + (outPutFileLength * 100) / totalBytes + " %");

            // Sleep
            try {
                Thread.sleep(50);
            } catch (InterruptedException ex) {
                LOGGER.log(Level.WARNING, "", ex);
            }
            }

            // 2 Seconds passed without response
            if (failCounter == 40)
            succeeded = false;
           // --------------------------End of Check the %100 Progress--------------------

        } catch (Exception ex) {
            succeeded = false;
            // Stop the External Thread which is updating the %100
            // progress
            stopThread = true;
            LOGGER.log(Level.WARNING, "DownloadService failed", ex);
        }







        //----------------------Finally------------------------------

        System.out.println("Trying to interrupt[shoot with an assault rifle] the copy Thread");

        // ---FORCE STOP COPY FILES
        if (copyThread != null && copyThread.isAlive()) {
            copyThread.interrupt();
            System.out.println("Done an interrupt to the copy Thread");

            // Run a Looping checking if the copyThread has stopped...
            while (copyThread.isAlive()) {
            System.out.println("Copy Thread is still Alive,refusing to die.");
            Thread.sleep(50);
            }
        }

        System.out.println("Download Service exited:[Value=" + succeeded + "] Copy Thread is Alive? "
            + (copyThread == null ? "" : copyThread.isAlive()));

        //---------------------- End of Finally------------------------------




        return succeeded;
        }

    };
    }

}

有趣的问题:

1-> java.lang.Thread.interrupt() 的作用是什么? https://stackoverflow.com/questions/3590000/what-does-java-lang-thread-interrupt-do


我强烈鼓励您使用FileChannel http://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html。 它有transferFrom() http://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html#transferFrom-java.nio.channels.ReadableByteChannel-long-long-当运行它的线程被中断时立即返回的方法。 (这里的 Javadoc 说它应该提出一个ClosedByInterruptException,但事实并非如此。)

try (FileChannel channel = FileChannel.open(Paths.get(...), StandardOpenOption.CREATE,
                                            StandardOpenOption.WRITE)) {
    channel.transferFrom(Channels.newChannel(new URL(...).openStream()), 0, Long.MAX_VALUE);
}

它还具有比其性能更好的潜力java.io选择。 (然而,事实证明,实施Files.copy()可以选择委托给此方法,而不是自己实际执行复制。)


下面是一个可重用 JavaFX 服务的示例,它允许您从 Internet 获取资源并将其保存到本地文件系统,如果操作时间过长,则会自动正常终止。

  • 服务任务(由createTask()) 是文件通道 API 的用户。
  • 独立ScheduledExecutorService https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html用于处理时间限制。
  • 始终坚持良好做法 https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm用于延伸Service https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Service.html.
  • 如果您选择使用如此高级的方法,您将无法跟踪任务的进度。
  • 如果连接不可用,transferFrom()最终应该返回而不抛出异常。

启动服务(可以从任何线程完成):

DownloadService downloadService = new DownloadService();
downloadService.setRemoteResourceLocation(new URL("http://speedtest.ftp.otenet.gr/files/test1Gb.db"));
downloadService.setPathToLocalResource(Paths.get("C:", "test1Gb.db"));
downloadService.start();

然后取消(否则时间到后会自动取消):

downloadService.cancel();

请注意,相同的服务可以重复使用,只需确保在重新启动之前重置它:

downloadService.reset();

这里是DownloadService class:

public class DownloadService extends Service<Void> {

    private static final long TIME_BUDGET = 2; // In seconds

    private final ScheduledExecutorService watchdogService =
            Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
                private final ThreadFactory delegate = Executors.defaultThreadFactory();

                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = delegate.newThread(r);
                    thread.setDaemon(true);
                    return thread;
                }
            });
    private Future<?> watchdogThread;

    private final ObjectProperty<URL> remoteResourceLocation = new SimpleObjectProperty<>();
    private final ObjectProperty<Path> pathToLocalResource = new SimpleObjectProperty<>();

    public final URL getRemoteResourceLocation() {
        return remoteResourceLocation.get();
    }

    public final void setRemoteResourceLocation(URL remoteResourceLocation) {
        this.remoteResourceLocation.set(remoteResourceLocation);
    }

    public ObjectProperty<URL> remoteResourceLocationProperty() {
        return remoteResourceLocation;
    }

    public final Path getPathToLocalResource() {
        return pathToLocalResource.get();
    }

    public final void setPathToLocalResource(Path pathToLocalResource) {
        this.pathToLocalResource.set(pathToLocalResource);
    }

    public ObjectProperty<Path> pathToLocalResourceProperty() {
        return pathToLocalResource;
    }

    @Override
    protected Task<Void> createTask() {
        final Path pathToLocalResource = getPathToLocalResource();
        final URL remoteResourceLocation = getRemoteResourceLocation();
        if (pathToLocalResource == null) {
            throw new IllegalStateException("pathToLocalResource property value is null");
        }
        if (remoteResourceLocation == null) {
            throw new IllegalStateException("remoteResourceLocation property value is null");
        }

        return new Task<Void>() {
            @Override
            protected Void call() throws IOException {
                try (FileChannel channel = FileChannel.open(pathToLocalResource, StandardOpenOption.CREATE,
                                                            StandardOpenOption.WRITE)) {
                    channel.transferFrom(Channels.newChannel(remoteResourceLocation.openStream()), 0, Long.MAX_VALUE);
                }
                return null;
            }
        };
    }

    @Override
    protected void running() {
        watchdogThread = watchdogService.schedule(() -> {
            Platform.runLater(() -> cancel());
        }, TIME_BUDGET, TimeUnit.SECONDS);
    }

    @Override
    protected void succeeded() {
        watchdogThread.cancel(false);
    }

    @Override
    protected void cancelled() {
        watchdogThread.cancel(false);
    }

    @Override
    protected void failed() {
        watchdogThread.cancel(false);
    }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

强制停止在外部线程上运行的 Java Files.copy() 的相关文章

随机推荐

  • 使用 Xceed PropertyGrid

    我是 C WPF 的初学者 正在尝试使用 Xceed PropertyGrid 他们在他们的网站上展示了一个示例
  • 删除 Woocommerce 中特定产品类别的添加购物车按钮

    我遇到如何从类别产品中删除购物车的问题 如果我将它应用于特定的 ID 或一般情况下的所有 ID 它就可以正常工作 但我无法对类别执行此操作 下面是我对此所做的代码 另外 我正在努力将相同的模式应用到相关文章部分 因此任何帮助将不胜感激 谢谢
  • 如何使用 opencv 和 Python 找到 ROI 内的轮廓?

    我试图找到图像特定区域的轮廓 是否可以只显示 ROI 内的轮廓 而不显示图像其余部分的轮廓 我在另一篇类似的文章中读到我应该使用面具 但我认为我使用得不正确 我对 openCV 和 Python 很陌生 所以非常感谢任何帮助 import
  • 检测网络下拉菜单是否将显示在屏幕外

    我有一个基于 CSS 的简单多级下拉菜单 第二或第三级可能会超出可见窗口 具有某些分辨率和窗口大小的组合 如果某些预构建的菜单控件检测到这种情况 它们只会向左而不是向右打开下拉菜单 我如何测试 使用 JS jQuery 这种情况 您可以使用
  • Playbook 位于子目录中,而不是紧邻 group_vars

    有谁知道如何将剧本放入文件夹中 但共享相同的角色 group vars 以及通常位于根目录的其他内容 这就是我想要的 root dir group vars roles inventory playbooks my playbook yml
  • 如何强制 Jackson 将字段值反序列化为小写

    我有一个公开 REST 端点的 spring 应用程序 让我们将其命名为 doAction 作为请求 它消耗对象 class Person private String name private String email 一些客户端可以通过
  • matplotlib 获取轴相对刻度位置

    我知道我可以通过以下方式获得 y 刻度的位置ax get yticks 顺便说一句 这是获得它们的最佳 正确方法吗 但我需要相对于轴限制的刻度位置 即在 0 和 1 之间 获得这个的最好方法是什么 我试过ax get yticks tran
  • 通过引用传递 int 与通过值传递 int 的好处?

    通过引用而不是值传递整数是否有性能优势 我这样说是因为如果您通过引用传递 您将创建一个 4 字节指针 但如果您通过值传递 您无论如何都会创建该值的 4 字节副本 那么它们都多占用了 4 个字节 对吗 是否可以使用强制转换通过引用传递 int
  • AttributeRouting - 从 RouteData 获取操作名称的正确方法

    我最近开始为我的操作方法使用属性路由 并且正在努力从 RouteData 获取操作名称 和 或 id 下面是我如何使用属性的示例 Route Edit id int public ActionResult Edit int id 之前我使用
  • ReSharper PdbNavigator 在符号服务器上找不到调试信息

    我们设置了 TeamCity 来为内部组件创建 NuGet 包和符号源包 符号源包正确包含 dll pdb 和源代码 一旦 TeamCity 创建了包 它们就会发布到我们的 NuGet SymbolSource 服务器 在 ReSharpe
  • 如何使用 POI SS 打开 .xlsx 文件?

    我正在尝试使用 POI SS 使用此代码打开 xlsx 文件 取自http poi apache org spreadsheet quick guide html ReadWriteWorkbook http poi apache org
  • 如何在Java中从sql模式生成orm映射类

    我有一个现有的 db sql 模式文件 是否可以在需要时生成并重新生成 DAO 实体以及访问它所需的所有其他帮助程序 客户端类 我不介意它会是什么 hibernate 其他 jpa 或其他东西 假设您 其他人仍在寻找解决方案 我刚刚遇到了同
  • 模块函数、静态方法、类方法、无装饰器:哪个习惯用法更Pythonic?

    我是一名 Java 开发人员 时常使用 Python 我最近偶然发现本文 http dirtsimple org 2004 12 python is not java html其中提到了 Java 程序员在学习 Python 时常犯的错误
  • 玩具操作系统文件系统[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我已经在 assembly c 中开发了一个运行基本终端的基本内核 我已将其设置为使用 grub 运行 iso 我想继续使用这个操作系
  • 了解 Spring MVC 中 @ModelAttribute 和 @RequestAttribute 注解的使用

    我对 Spring MVC 还很陌生 目前我正在学习Spring MVC 展示 https github com spring projects spring mvc showcase 演示了 Spring MVC Web 框架的功能 我无
  • 如何以编程方式停止水平滚动?

    我在cocos2d的CCLayer中有一个UITextView textView 文本在水平和垂直方向上滚动 但是 我需要它仅垂直滚动和弹跳 如何以编程方式停止水平滚动 UITextView textView UITextView allo
  • 如何将图像放入颤振图像中

    https i stack imgur com w5mLQ png https i stack imgur com w5mLQ png 就像我们在大图片中看到的一个小圆形图像一样 以及如何如图所示排列文字 https i stack img
  • ZoneInfoNotFoundError:“未找到带有 utc 密钥的时区”

    当尝试在浏览器上加载我的网页时 我收到了消息 服务器发生错误 请联系管理员 当我回去检查我的终端时 我看到了这条消息 zoneinfo common ZoneInfoNotFoundError 未找到具有关键 UTC 的时区 我检查过但不知
  • 如何使用变量来避免为工作表中的所有列创建一组变量?

    当单元格更改时 我试图让我的工作表在两个方向上自动重新计算计划内的一组日期 该代码工作正常 但我需要添加更多列 而且我真的不想多次复制 粘贴 查找 替换负载 我相当确定我可以使用变量来做到这一点 只需查找列标识符并以某种方式将其输入代码中
  • 强制停止在外部线程上运行的 Java Files.copy()

    这里的答案似乎是 Java 8 之前的有效解决方案 如何在Java中取消Files copy https stackoverflow com q 17083896 但现在不行了 因为ExtendedCopyOption INTERRUPTI