I have a web application. I am using java and spring. The application can create a file and send it to the browser, this is working fine. The way I do it is:
I create the file in a Services class, and the method returns the address to the controller. The controller then sends the file, and it is downloaded correctly. The code for the controller method is this.
@RequestMapping("/getFile")
public @ResponseBody
FileSystemResource getFile() {
String address = Services.createFile();
response.setContentType("application/vnd.ms-excel");
return new FileSystemResource(new File (address));
}
The problem is that the file is saved in the server, and after many requests it will have a lot of files. I have to delete them manually. The question is: How can I delete this file after sending it? or Is there a way to send the file without saving it in the server?
Don't use @ResponseBody
. Have Spring inject the HttpServletResponse
and write directly to its OutputStream
.
@RequestMapping("/getFile")
public void getFile(HttpServletResponse response) {
String address = Services.createFile();
File file = new File(address);
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment; filename=" + file.getName());
OutputStream out = response.getOutputStream();
FileInputStream in = new FileInputStream(file);
// copy from in to out
IOUtils.copy(in,out);
out.close();
in.close();
file.delete();
}
I haven't added any exception handling. I leave that to you.
FileSystemResource
is really just is a wrapper for FileInputStream
that's used by Spring.
Or, if you want to be hardcore, you could make your own FileSystemResource
implementation with its own getOutputStream()
method that returns your own implementation of FileOutputStream
that deletes the underlying file when you call close()
on it.
So I decided to take Sotirious's suggestion for a "hardcore" way. It is pretty simple, but has one problem. If user of that class opens input stream once to check something and closes it, it will not be able to open it again since file is deleted on close. Spring does not seem to do that, but you will need to check after every version upgrade.
public class DeleteAfterReadeFileSystemResource extends FileSystemResource {
public DeleteAfterReadeFileSystemResource(File file) {
super(file);
}
@Override
public InputStream getInputStream() throws IOException {
return new DeleteOnCloseFileInputStream(super.getFile());
}
private static final class DeleteOnCloseFileInputStream extends FileInputStream {
private File file;
DeleteOnCloseFileInputStream(File file) throws FileNotFoundException {
super(file);
this.file = file;
}
@Override
public void close() throws IOException {
super.close();
file.delete();
}
}
}
A minor adaption to this answer.
Using a InputStreamResource
instead of FileSystemResource
makes this a little shorter.
public class CleanupInputStreamResource extends InputStreamResource {
public CleanupInputStreamResource(File file) throws FileNotFoundException {
super(new FileInputStream(file) {
@Override
public void close() throws IOException {
super.close();
Files.delete(file.toPath());
}
});
}
}