如何在网络应用程序中发送文件后删除文件?

问题描述 投票:0回答:6

我有一个网络应用程序。我正在使用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));
}

问题是文件保存在服务器中,多次请求后就会有很多文件。我必须手动删除它们。问题是:这个文件发送后如何删除?或者有没有办法发送文件而不将其保存在服务器中?

java spring file web-applications file-upload
6个回答
33
投票

不要使用

@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()
时会删除底层文件。


12
投票

所以我决定采用 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();
        }
    }
}

11
投票

这个答案稍作修改。

使用

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());
            }
        });
    }
}

4
投票

您可以使用匿名类编写 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());
            }
        };
    }
}

1
投票

使用了这个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());
    }
}

0
投票

这是在控制器方法中使用 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());
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.