【问题标题】:How to delete a file after sending it in a web app?在网络应用程序中发送文件后如何删除文件?
【发布时间】:2013-07-29 19:14:59
【问题描述】:

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


    【解决方案1】:

    不要使用@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 的包装器。

    或者,如果您想成为铁杆,您可以使用自己的getOutputStream() 方法创建自己的FileSystemResource 实现,该方法返回您自己的FileOutputStream 实现,当您在其上调用close() 时删除底层文件.

    【讨论】:

    • 在 while 循环中,您从 in 读取一个 byte[] 并将其写入 out。寻找 Java IO 教程。
    • 最好不要关闭响应输出流。
    • 这个解决方案在文件很大的时候会出现问题。 IOUtils.copy(in,out) 抛出 OutOfMemoryException。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      this answer 的小改动。

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

      【讨论】:

      • 请注意,Spring 不会处理 InputStreamResources 的范围标头,仅适用于 FileSystemResources
      【解决方案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());
                  }
              };
          }
      }
      

      【讨论】:

        【解决方案5】:

        使用了这个answer 并添加了一些修改。工作至今。由于我的知识有限,我无法为我的自定义 inputStream 创建动态代理。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-12-21
          • 1970-01-01
          • 2021-11-07
          • 1970-01-01
          • 2012-11-09
          • 2012-10-23
          • 2023-03-25
          相关资源
          最近更新 更多