Forcer l'arrêt Fichiers Java.copy () tournant sur le thread externe
la réponse ici semblait être une solution valable avant Java 8: comment annuler des fichiers.copier() en Java?
mais maintenant ça ne marche pas, parce que ExtendedCopyOption.INTERRUPTIBLE
est privé.
fondamentalement, je dois télécharger un fichier à partir de certains URL
et le sauvegarder dans mon système de fichiers local en utilisant Files.copy()
.
Actuellement, J'utilise un service JavaFX parce que j'ai besoin de montrer les progrès dans un ProgressBar
.
cependant, je ne sais pas comment bloquer le fil tournant Files.copy()
si l'opération prend trop de temps.
Utiliser Thread.stop()
n'est pas souhaité. Même Thread.interrupt()
échoue.
je veux aussi que l'opération se termine gracieusement si la connexion internet devient indisponible.
pour tester le cas quand aucune connexion internet n'est disponible, je retire mon câble ethernet et je le remets après 3 deuxième.
Malheureusement, Files.copy()
ne retourne que lorsque j'ai remis le câble ethernet, alors que je voudrais qu'elle échoue immédiatement.
comme je peux le voir, en interne Files.copy()
exécute une boucle, qui empêche le fil de sortir.
Tester
(téléchargement 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
:
en utilisant @sillyfly commentaire avec FileChannel
et supprimer File.copy
semble fonctionner seulement avec l'appel Thread.interrupt()
, mais il ne sort pas lorsque l'internet n'est pas disponible..
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;
}
};
}
}
questions intéressantes:
3 réponses
je vous encourage fortement à utiliser un FileChannel
.
Il a la méthode transferFrom()
qui retourne immédiatement quand le fil courant il est interrompu.
(Le Javadoc dit ici qu'il devrait soulever un ClosedByInterruptException
, mais il ne le fait pas.)
try (FileChannel channel = FileChannel.open(Paths.get(...), StandardOpenOption.CREATE,
StandardOpenOption.WRITE)) {
channel.transferFrom(Channels.newChannel(new URL(...).openStream()), 0, Long.MAX_VALUE);
}
il a également le potentiel de faire beaucoup mieux que son java.io
alternative.
(Toutefois, il s'avère que la mise en œuvre de Files.copy()
mai choisissez de déléguer à cette méthode au lieu d'exécuter la copie elle-même.)
voici un exemple de service JavaFX réutilisable qui vous permet de récupérer une ressource à partir d'internet et de la sauvegarder dans votre système de fichiers local, avec une fin gracieuse automatique si l'opération prend trop de temps.
- la tâche de service (générée par
createTask()
) est l'utilisateur de l'API file-channel. - Un
ScheduledExecutorService
distinct est utilisé pour gérer la contrainte de temps. - toujours s'en tenir au bonnes pratiques pour étendre
Service
. - Si vous choisissez d'utiliser une telle méthode, vous ne serez pas en mesure de retracer la progression de la tâche.
- si la connexion devient indisponible,
transferFrom()
devrait éventuellement revenir sans lancer exception.
pour démarrer le service (peut être fait à partir de n'importe quel fil):
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();
et ensuite de l'annuler (sinon elle sera automatiquement annulée après l'expiration du délai):
downloadService.cancel();
notez que le même service peut être réutilisé, assurez-vous de le réinitialiser avant de recommencer:
downloadService.reset();
Voici la classe DownloadService
:
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);
}
}
il y a un aspect important qui n'est pas couvert par les autres réponses/commentaires; et c'est une fausse hypothèse de votre part:
ce que je veux, c'est qu'il tombe en panne immédiatement quand il n'y a pas de connexion internet.
ce n'est pas si facile. La machine TCP stack/state est en fait assez compliquée et dépend de votre contexte (type D'OS, implémentation de la pile TCP, paramètres du noyau, etc.)...), il peut y avoir des situations où une partition réseau a lieu et un expéditeur ne remarque pas pendant 15 ou plus minutes . Écoutez ici pour plus de détails à ce sujet.
en d'autres termes:" juste tirer la prise "n'est en aucun cas égal à" casser immédiatement " votre connexion TCP existante. Et juste pour Info: vous n'avez pas besoin de brancher les câbles manuellement pour simuler des pannes de réseau. Dans une configuration de test raisonnable, des outils comme iptables alias pare-feu peut le faire pour vous.
vous semblez avoir besoin d'un accès HTTP asynchrone/annulable qui peut être difficile.
le problème est que si la lecture décroche en attendant plus de données (le câble est tiré) il ne cessera pas jusqu'à ce que la socket meurt ou de nouvelles données viennent dans.
il y a quelques chemins que vous pouvez suivre, bricolage avec les usines de socket pour définir un bon timeout, en utilisant le client http avec timeouts et autres.
je voudrais jeter un oeil à Apache Http Composants qui possède un HTTP non bloquant basé sur des Sockets java NIO.