link
link

Reputation: 1676

Handle temporary file in try with resources

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

Answers (5)

Xavier Dury
Xavier Dury

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

Ian
Ian

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

zapl
zapl

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

Aaron
Aaron

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

Jazzepi
Jazzepi

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

Related Questions