Reputation: 1676
For my application, I have to write a method that takes an InputStream
as argument, writes the content to a temporary file, performs some operations and finally deletes the temporary.
This is what I have so far:
public void myMethod(InputStream in, String name) {
//...
Path path = Paths.get("./tmp/benchmarks/" + name + ".zip")
try {
Files.copy(in, path);
//operations...
} catch (IOException e) {
//error handling for copy...
} finally {
try {
Files.delete(path));
} catch (IOException e) {
//error handling for delete...
}
}
//...
}
It does the job, but it also looks really ugly. I was wondering if there was some way to use try-with-resources
to handle this more gracefully.
Is it possible somehow?
UPDATE: I wrote an on-the-fly solution in ten minutes. It looks like this:
public class TemporaryFileHandler implements AutoCloseable {
private File file;
public TemporaryFileHandler(final InputStream in, final Path path) throws IOException {
Files.copy(in, path);
this.file = new File(path.toString());
}
public File getFile() { return file; }
@Override
public void close() throws IOException {
Files.delete(file.toPath());
}
}
I'm sure it's not the best, but it does the job for now. If anyone has suggestions on how to improve this in any way, suggestions are more than welcome.
Upvotes: 5
Views: 14763
Reputation: 1571
You could do something like this in java 8:
Path path = Files.createTempFile("temp-", ".tmp");
try (Closeable onClose = () -> Files.delete(path)) {
...
}
but this is really the same as:
Path path = Files.createTempFile("temp-", ".tmp");
try {
...
} finally {
Files.delete(path);
}
Upvotes: 3
Reputation: 7558
I had a similar problem where temporary ZIP files weren't being deleted. My assumption is that the output streams weren't being closed before the code attempted to delete the temp file.
My solution, which uses a nested try, isn't as elegant but it should guarantee that the streams are closed beforehand.
Before
File out = // get file;
try(
FileOutputStream fos = new FileOutputStream(out);
ZipOutputStream zos = new ZipOutputStream(fos);
){
// Create ZIP file and deliver to client using HTTPServletResponse
}
finally{
if (out != null){
out.delete();
}
}
After
File out = // get file;
try{
try(
FileOutputStream fos = new FileOutputStream(out);
ZipOutputStream zos = new ZipOutputStream(fos);
){
// Create ZIP file and deliver to client using HTTPServletResponse
}
}
finally{
if (out != null){
out.delete();
}
}
Upvotes: 0
Reputation: 63955
I think with a little helper / wrapper like
public class AutoDeletingTempFile implements AutoCloseable {
private final Path file;
public AutoDeletingTempFile() throws IOException {
file = Files.createTempFile(null, null);
}
public Path getFile() {
return file;
}
@Override
public void close() throws IOException {
Files.deleteIfExists(file);
}
}
which gets closed and deletes the file it wraps you get a nice and short syntax:
public void myMethod(InputStream in, String name) {
try (AutoDeletingTempFile wrapper = new AutoDeletingTempFile()) {
//Files.copy(in, wrapper.getFile());
//operations...
} catch (IOException e) {
//error handling for copy...
// + temp file creation
}
}
or a neat little Closable
via lambdas
public void myMethod(InputStream in, Path existingFile, String name) {
try (Closeable closable = () -> Files.deleteIfExists(existingFile)) {
// ...
} catch (IOException e) {
//
}
}
Upvotes: 11
Reputation: 574
Files.createTempFile allows you to create a temporary file in the default temporary directory of the JVM. This does not mean that the file is automatically deleted or marked for deletion using File.deleteOnExit()
. The developer is responsible for managing the lifecycle of temporary files.
The name of the file is a vector for security vulnerabilities. Only use the name for display in the UI for validation and feedback. Do not use untrusted user input for file names.
It is not possible to manage the lifecycle of file with try-with-resources using java.io.File or java.nio.file.Path. The InputStream is manageable with the try-with-resources.
public void myMethod(InputStream in) {
Path path = null;
try (InputStream stream = in) {
path = Files.createTempFile(null, ".zip");
Files.copy(stream, path);
} catch (IOException e) {
//error handling for copy...
} finally {
if (path != null) {
try {
Files.delete(path));
} catch (IOException e) {
//error handling for delete...
}
}
}
}
Upvotes: 1
Reputation: 5480
Try-with-resource just calls the close method on classes implementing the interfaces implementing java.lang.AutoCloseable
. There's nothing from stopping you from creating a File implementation that implements AutoCloseable
and deletes itself when close() is called.
You can also call deleteOnExit()
on the file to have the JVM delete it when it exits. This is only appropriate if you're okay with waiting until the JVM is done to delete your temporary file. This probably wouldn't be a good idea for a long running JVM like a Java webapp.
Upvotes: 4