I have implemented an API /transferFile
that downloads a file from a server and writes it to a specific location. To ensure the file transfer runs in the separate thread, I am using ExecutorService
with a single-thread executor like this:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future> future = executorService.submit(() -> {
return transferFileMethod(someparameters);
});
The transferFileMethod
looks like this:
Map transferFileMethod(someparameters) {
// Downloading from server
HttpURLConnection urlConnection = (HttpURLConnection) requestUrl.openConnection();
InputStream inputStream = urlConnection.getInputStream();
// Writing to specific location
try (BufferedOutputStream fileOutputStream = new BufferedOutputStream(new FileOutputStream(filePath, true))) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer, 0, 4096)) != -1) {
fileOutputStream.write(buffer, 0, bytesRead);
}
}
}
Problem:
If the user cancels the API call while the file is being transferred, the background process continues to write the file. I want to stop the file transfer immediately when the user cancels the API call.
I have implemented an API /transferFile
that downloads a file from a server and writes it to a specific location. To ensure the file transfer runs in the separate thread, I am using ExecutorService
with a single-thread executor like this:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future> future = executorService.submit(() -> {
return transferFileMethod(someparameters);
});
The transferFileMethod
looks like this:
Map transferFileMethod(someparameters) {
// Downloading from server
HttpURLConnection urlConnection = (HttpURLConnection) requestUrl.openConnection();
InputStream inputStream = urlConnection.getInputStream();
// Writing to specific location
try (BufferedOutputStream fileOutputStream = new BufferedOutputStream(new FileOutputStream(filePath, true))) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer, 0, 4096)) != -1) {
fileOutputStream.write(buffer, 0, bytesRead);
}
}
}
Problem:
If the user cancels the API call while the file is being transferred, the background process continues to write the file. I want to stop the file transfer immediately when the user cancels the API call.
Share Improve this question edited 15 hours ago Amit K. asked yesterday Amit K.Amit K. 311 silver badge6 bronze badges 3 |1 Answer
Reset to default 0I have had to add another endpoint to receive the canceled signal, and exit the while, so far, I can't get a SocketException
or any other cancel signal.
@RestController
public class CancelTestController {
private Future<?> future;
private final AtomicBoolean atomicCancel = new AtomicBoolean(true);
@GetMapping(value = "/cancel")
public String cancel() {
if(future != null) {
future.cancel(true);
this.atomicCancel.set(false);
System.out.println("inputStream is closed");
}
return "cancel"; //dummy response
}
@GetMapping(value = "/download")
public String test() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
this.future = executorService.submit(() -> {
try {
this.transferFileMethod();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
System.out.println("some error " + e.getMessage());
}
});
return "ok"; //dummy response
}
private void transferFileMethod() throws IOException {
this.atomicCancel.set(true);
HttpURLConnection urlConnection = (HttpURLConnection) new java.URL("https://filesamples/samples/document/txt/sample1.txt").openConnection();
final InputStream inputStream = urlConnection.getInputStream();
try (BufferedOutputStream fileOutputStream = new BufferedOutputStream(new FileOutputStream("src/main/resources/sample1.txt"))) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer, 0, 4096)) != -1 && this.atomicCancel.get()) {
fileOutputStream.write(buffer, 0, bytesRead);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
The
Thread.sleep(...)
is for testing purposes only, I should not use it, and I should be able to cancel since it is a fast download in this test of mine.
java
sockets are non-interruptible. If you rewrite it all using aSocketChannel
, you can interrupt the thread, which will cause the read to throwClosedByInterruptException
. This is better than a flag because it will interrupt the pending read immediately, rather than letting the thread run until the read returns. However you will have to deal with all the HTTP protocol stuff yourself. – user207421 Commented 14 hours ago