我有一个网络应用程序。我正在使用java和spring。应用程序可以创建一个文件并将其发送到浏览器,这工作正常。我的做法是:
我在 Services 类中创建文件,该方法将地址返回到控制器。然后控制器发送文件,并且文件被正确下载。控制器方法的代码是这样的。
@RequestMapping("/getFile")
public @ResponseBody
FileSystemResource getFile() {
String address = Services.createFile();
response.setContentType("application/vnd.ms-excel");
return new FileSystemResource(new File (address));
}
问题是文件保存在服务器中,多次请求后就会有很多文件。我必须手动删除它们。问题是:这个文件发送后如何删除?或者有没有办法发送文件而不将其保存在服务器中?
不要使用
@ResponseBody
。让 Spring 注入 HttpServletResponse
并直接写入其 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();
}
我没有添加任何异常处理。我把这个留给你了。
FileSystemResource
实际上只是 Spring 使用的 FileInputStream
的包装。
或者,如果您想成为硬核,您可以使用自己的
FileSystemResource
方法创建自己的 getOutputStream()
实现,该方法返回您自己的 FileOutputStream
实现,当您调用 close()
时会删除底层文件。
所以我决定采用 Sotirious 的建议,采用“硬核”方式。这很简单,但有一个问题。如果该类的用户打开输入流一次以检查某些内容并关闭它,则它将无法再次打开它,因为文件在关闭时被删除。 Spring似乎没有这样做,但是您需要在每次版本升级后进行检查。
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();
}
}
}
对这个答案稍作修改。
使用
InputStreamResource
代替 FileSystemResource
会使这个时间变短一些。
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());
}
});
}
}
您可以使用匿名类编写 Mag 的解决方案,如下所示:
new FileSystemResource(file) {
@Override
public InputStream getInputStream() throws IOException {
return new FileInputStream(file) {
@Override
public void close() throws IOException {
super.close();
Files.delete(file.toPath());
}
};
}
}
使用了这个answer并添加了一些修改。工作至今。由于我的知识有限,我无法为我的自定义输入流创建动态代理。
import static org.apache.commons.io.FileUtils.deleteQuietly;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import org.springframework.core.io.FileSystemResource;
import lombok.RequiredArgsConstructor;
public final class AutoDeleteFileSystemResource extends FileSystemResource {
public AutoDeleteFileSystemResource(Path filePath) {
super(filePath);
}
@RequiredArgsConstructor
private static final class AutoDeleteStream extends InputStream {
private final File file;
private final InputStream original;
@Override
public int read() throws IOException {
return original.read();
}
@Override
public void close() throws IOException {
original.close();
deleteQuietly(file);
}
@Override
public int available() throws IOException {
return original.available();
}
@Override
public int read(byte[] b) throws IOException {
return original.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return original.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return original.skip(n);
}
@Override
public boolean equals(Object obj) {
return original.equals(obj);
}
@Override
public int hashCode() {
return original.hashCode();
}
@Override
public synchronized void mark(int readlimit) {
original.mark(readlimit);
}
@Override
public boolean markSupported() {
return original.markSupported();
}
@Override
public synchronized void reset() throws IOException {
original.reset();
}
@Override
public String toString() {
return original.toString();
}
}
/**
* @see org.springframework.core.io.FileSystemResource#getInputStream()
*/
@Override
public InputStream getInputStream() throws IOException {
return new AutoDeleteStream(getFile(), super.getInputStream());
}
}
这是在控制器方法中使用 StreamingResponseBody 的简单方法:
File file = getFile();
return ResponseEntity.ok()
.headers(headers)
.contentLength(length)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(os -> {
Files.copy(file.toPath(), os);
Files.delete(file.toPath());
});
lambda 是以下表达式的简写:
new StreamingResponseBody() {
@Override
public void writeTo(OutputStream os) throws IOException {
Files.copy(file.toPath(), os);
Files.delete(file.toPath());
}
}