I've got a server application that uses JAX-RS. I'm adding an endpoint to it which will retrieve an object (aka a file) from a S3 bucket, save it locally in a temp folder, serve up this file, and then delete it from the temp folder. So one of the methods I'm adding looks something like this:
@GET
@Path("/download-file")
public Response downloadFile() {
File tempFile = null;
try {
tempFile = s3FileDownloader.downloadFile();
Response response = Response.ok(tempFile)
.header("Content-Disposition", "attachment; filename=myfile.dat")
.header("Content-Type", "application/octet-stream")
.build();
return response;
} finally {
if (tempFile != null) {
tempFile.delete();
}
}
}
However, that doesn't work, because it seems it's executing the delete()
method in the finally block before it renders the file. When I hit the endpoint, I get a 500 error from Tomcat stating "the system cannot find the file specified."
If I change it to tempFile.deleteOnExit()
then it works. However this is not ideal as the server is not meant to ever really exit, besides on reboots which occur very infrequently. How can I get it to render the output before it hits my delete statement? Or is there a better way of going about this?
UPDATE: Adding the code I have for my FileDownloader class below, as requested.
public class FileDownloader {
private final AmazonS3Client s3Client;
private final String bucketName;
public FileDownloader(AmazonS3Client s3Client, String bucketName) {
this.s3Client = s3Client;
this.bucketName = bucketName;
}
public File downloadFile(String key) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
S3Object data = s3Client.getObject(bucketName, key);
inputStream = data.getObjectContent();
File file = File.createTempFile(System.getProperty("java.io.tmpdr"), "." + FileNameUtils.getExtension(key));
outputStream = new FileOutputStream(file);
int read;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
return file;
} catch (AmazonS3Exception|IOException ex) {
// log the exception
// throw a custom exception type instead
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException) { }
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException ex) { }
}
}
}