【问题标题】:Spring MVC : large files for download, OutOfMemoryExceptionSpring MVC:大文件下载,OutOfMemoryException
【发布时间】:2013-04-04 00:26:42
【问题描述】:

如何通过spring控制器提供大文件下载?我关注了一些关于类似主题的讨论:

Downloading a file from spring controllers

但是对于 300mb - 600mb 的大文件,这些解决方案会失败。 我在最后一行收到 OutOfMemoryException :

@RequestMapping(value = "/file/{dummyparam}.pdf", method = RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
public @ResponseBody byte[] getFile(@PathVariable("dummyparam") String dummyparam, HttpServletResponse response) {
.
.       
InputStream is = new FileInputStream(resultFile);

response.setHeader("Content-Disposition", "attachment; filename=\"dummyname " + dummyparam + ".pdf\"");
.
.         
return IOUtils.toByteArray(is);

我的(天真的)假设是 IOUtils 甚至可以处理大文件,但这显然不会发生。有什么方法可以在下载过程中将文件拆分成块?文件通常约为 300 - 600mb 大。最大并发下载数估计为 10。

简单的方法是将文件链接为网络服务器目录中的静态内容,但我们想尝试在我们的 Spring 应用程序中进行。

【问题讨论】:

    标签: java spring-mvc download


    【解决方案1】:

    这是因为您正在将整个文件读入内存,请改用缓冲读写。

    @RequestMapping(value = "/file/{dummyparam}.pdf", method = RequestMethod.GET, produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public void getFile(@PathVariable("dummyparam") String dummyparam, HttpServletResponse response) {
    
    
        InputStream is = new FileInputStream(resultFile);
    
        response.setHeader("Content-Disposition", "attachment; filename=\"dummyname " + dummyparam + ".pdf\"");
    
    
        int read=0;
        byte[] bytes = new byte[BYTES_DOWNLOAD];
        OutputStream os = response.getOutputStream();
    
        while((read = is.read(bytes))!= -1){
            os.write(bytes, 0, read);
        }
        os.flush();
        os.close(); 
    }
    

    【讨论】:

    • P 在上述实现中 BYTES_DOWNLOAD 的大小应该是多少
    • 改用IOUtils.copyLarge(inputStream, response.getOutputStream());,其中缓冲区大小由 DEFAULT_BUFFER_SIZE 给出。根据 jar 版本,大小可能从 4KB 到 8KB 不等。
    【解决方案2】:

    对于 Spring ,需要在 ResponseEntity 中使用 InputStreamResource 类。

    演示代码:

            MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName);
            System.out.println("fileName: " + fileName);
            System.out.println("mediaType: " + mediaType);
    
            File file = new File(DIRECTORY + "/" + fileName);
            InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
    
            return ResponseEntity.ok()
                    // Content-Disposition
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
                    // Content-Type
                    .contentType(mediaType)
                    // Contet-Length
                    .contentLength(file.length()) //
                    .body(resource);
        }
    

    参考链接:https://o7planning.org/en/11765/spring-boot-file-download-example

    【讨论】:

    • InputStreamResource 是 Spring 的 Resource 接口最通用的实现之一,仅用于包装已经打开的流,我们无法控制其创建过程。我宁愿考虑使用 ClassPathResourceFileSystemResource 取决于文件系统中的文件位置。
    猜你喜欢
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 2015-01-17
    • 1970-01-01
    • 2011-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多