ответ здесь, казалось, правильное решение, прежде чем Java 8: How to cancel Files.copy() in Java?Force остановка Java Files.copy() работает на внешней резьбой
Но теперь он не работает, потому что ExtendedCopyOption.INTERRUPTIBLE
является частным.
В принципе, мне нужно, чтобы загрузить файл с некоторой заданной URL
и сохранить его в моей локальной файловой системы с помощью Files.copy()
. В настоящее время я использую службу JavaFX, потому что мне нужно показать прогресс в ProgressBar
.
Однако, я не знаю, как заблокировать поток, выполняющийся Files.copy()
, если операция занимает слишком много времени. Использование Thread.stop()
по крайней мере не требуется. Даже Thread.interrupt()
не удается.
Я также хочу, чтобы операция прекратилась изящно, если интернет-соединение становится недоступным.
Чтобы проверить случай, когда интернет-соединение недоступно, я удаляю свой кабель Ethernet и верну его обратно через 3 секунды. К сожалению, Files.copy()
возвращает только тогда, когда я вернул кабель Ethernet, хотя мне бы хотелось, чтобы он немедленно сработал.
Как я вижу, внутри Files.copy()
работает петля, которая предотвращает выход нити.
Tester
(Загрузка OBS Студия ехе):
/**
* @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->What does java.lang.Thread.interrupt() do?
Если нить не закончилась в цикле, она автоматически остановится. Теперь, если поток продолжает работать, это означает, что он находится в цикле, и вы, похоже, хотите использовать «Thread».interrupt() ', чтобы остановить его. Но это может работать, только если в потоке есть прерывание, например 'Thread.pause (x)'. В противном случае JVM не знает, когда прервать поток, это было бы не здорово, если бы это произошло в середине операции записи файлов. – Voltboyy
Я не уверен, но похоже, что вам нужно будет использовать что-то вроде [FileChannel'] (https://docs.oracle.com/javase/8/docs/api/java/nio/channels/ FileChannel.html), аналогично тому, что описано в [этом вопросе] (http://stackoverflow.com/questions/35875142/how-to-cancel-files-copy-in-java-while-not-using-a-non -api-класс/35877509). Это похоже на излишний, хотя, возможно, есть более простой способ. – Itai
@sillyfly Я думаю, что ваш вариант будет работать отлично, и для тестирования потери соединения может использоваться тайм-аут, несколько как в [этом вопросе] (http://stackoverflow.com/questions/6529733/how-to-use-urlconnection- Тайм-аут). – Voltboyy