从Spring启动休息服务下载文件

kir*_*ran 57 java rest spring

我试图从Spring启动休息服务下载文件.

@RequestMapping(path="/downloadFile",method=RequestMethod.GET)
    @Consumes(MediaType.APPLICATION_JSON_VALUE)
    public  ResponseEntity<InputStreamReader> downloadDocument(
                String acquistionId,
                String fileType,
                Integer expressVfId) throws IOException {
        File file2Upload = new File("C:\\Users\\admin\\Desktop\\bkp\\1.rtf");
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        InputStreamReader i = new InputStreamReader(new FileInputStream(file2Upload));
        System.out.println("The length of the file is : "+file2Upload.length());

        return ResponseEntity.ok().headers(headers).contentLength(file2Upload.length())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(i);
        }
Run Code Online (Sandbox Code Playgroud)

当我尝试从浏览器下载文件时,它会开始下载,但始终会失败.导致下载失败的服务有什么问题吗?

fat*_*ddy 109

选项1使用InputStreamResource

给定InputStream的资源实现.

只有在没有其他特定资源实现适用时才应使用.特别是,在可能的情况下,更喜欢ByteArrayResource或任何基于文件的资源实现.

@RequestMapping(path = "/download", method = RequestMethod.GET)
public ResponseEntity<Resource> download(String param) throws IOException {

    // ...

    InputStreamResource resource = new InputStreamResource(new FileInputStream(file));

    return ResponseEntity.ok()
            .headers(headers)
            .contentLength(file.length())
            .contentType(MediaType.parseMediaType("application/octet-stream"))
            .body(resource);
}
Run Code Online (Sandbox Code Playgroud)

Option2作为InputStreamResource的文档建议 - 使用ByteArrayResource:

@RequestMapping(path = "/download", method = RequestMethod.GET)
public ResponseEntity<Resource> download(String param) throws IOException {

    // ...

    Path path = Paths.get(file.getAbsolutePath());
    ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));

    return ResponseEntity.ok()
            .headers(headers)
            .contentLength(file.length())
            .contentType(MediaType.parseMediaType("application/octet-stream"))
            .body(resource);
}
Run Code Online (Sandbox Code Playgroud)

  • @TulsiJain添加Content-Disposition HttpHeader:`HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename = myDoc.docx");` (10认同)
  • 为了防止你使用普通的Spring而不是Spring Boot,你需要确保将一个`ResourceHttpMessageConverter`实例添加到你的HttpMessageConverters列表中.创建一个扩展`WebMvcConfigurerAdapter`的`@Configuration`类,实现configureMessageConverters()方法并添加`converters.add(new ResourceHttpMessageConverter());`line (6认同)
  • 我正在尝试为 word 文档 .doc 格式执行此操作,但是下载时格式消失了,下载的文件没有文件扩展名,文件名是下载时的响应。有什么建议吗? (4认同)
  • 问题:选项 1 似乎没有关闭流。魔力在哪里?选项 2 似乎是在发送之前将完整的文件加载到内存中。正确的?备择方案?谢谢! (4认同)
  • 对于大文件 ByteArrayResource 可以工作吗?它不会占用整个堆空间吗?或者我们应该选择 StreamingResponseBody 这样就不会发生内存不足的情况? (3认同)

Fel*_*ati 15

我建议使用StreamingResponseBody,因为有了它,应用程序可以直接写入响应 (OutputStream),而无需阻止 Servlet 容器线程。如果您正在下载一个非常大的文件,这是一个很好的方法。

@GetMapping("download")
public StreamingResponseBody downloadFile(HttpServletResponse response, @PathVariable Long fileId) {

    FileInfo fileInfo = fileService.findFileInfo(fileId);
    response.setContentType(fileInfo.getContentType());
    response.setHeader(
        HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + fileInfo.getFilename() + "\"");

    return outputStream -> {
        int bytesRead;
        byte[] buffer = new byte[BUFFER_SIZE];
        InputStream inputStream = fileInfo.getInputStream();
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

Ps.: 使用StreamingResponseBody 时,强烈建议配置 Spring MVC 中使用的 TaskExecutor 来执行异步请求。TaskExecutor 是一个接口,它抽象了一个 Runnable 的执行。

更多信息:https : //medium.com/swlh/streaming-data-with-spring-boot-restful-web-service-87522511c071

  • 不,因为它是自动关闭的。只需检查我们是否返回一个 lambda,框架就会随后调用它。 (2认同)

Raj*_*esh 8

下面的示例代码对我有用,可能会对某人有所帮助。

import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
@RequestMapping("/app")
public class ImageResource {

    private static final String EXTENSION = ".jpg";
    private static final String SERVER_LOCATION = "/server/images";

    @RequestMapping(path = "/download", method = RequestMethod.GET)
    public ResponseEntity<Resource> download(@RequestParam("image") String image) throws IOException {
        File file = new File(SERVER_LOCATION + File.separator + image + EXTENSION);

        HttpHeaders header = new HttpHeaders();
        header.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=img.jpg");
        header.add("Cache-Control", "no-cache, no-store, must-revalidate");
        header.add("Pragma", "no-cache");
        header.add("Expires", "0");

        Path path = Paths.get(file.getAbsolutePath());
        ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));

        return ResponseEntity.ok()
                .headers(header)
                .contentLength(file.length())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(resource);
    }

}
Run Code Online (Sandbox Code Playgroud)


小智 8

我想分享一种使用 JavaScript (ES6)、ReactSpring Boot后端下载文件的简单方法:

  1. Spring Boot Rest 控制器

来自org.springframework.core.io.Resource 的资源

    @SneakyThrows
    @GetMapping("/files/{filename:.+}/{extraVariable}")
    @ResponseBody
    public ResponseEntity<Resource> serveFile(@PathVariable String filename, @PathVariable String extraVariable) {

        Resource file = storageService.loadAsResource(filename, extraVariable);
        return ResponseEntity.ok()
               .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
               .body(file);
    }
Run Code Online (Sandbox Code Playgroud)
  1. React,使用AXIOS 的API 调用

将 responseType 设置为arraybuffer以指定响应中包含的数据类型。

export const DownloadFile = (filename, extraVariable) => {
let url = 'http://localhost:8080/files/' + filename + '/' + extraVariable;
return axios.get(url, { responseType: 'arraybuffer' }).then((response) => {
    return response;
})};
Run Code Online (Sandbox Code Playgroud)

最后一步>下载
的帮助下 JS文件下载 ,你可以触发浏览器,如果它被下载到数据保存到文件中。

DownloadFile('filename.extension', 'extraVariable').then(
(response) => {
    fileDownload(response.data, filename);
}
, (error) => {
    // ERROR 
});
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您需要从服务器的文件系统下载一个巨大的文件,那么ByteArrayResource可以占用所有 Java 堆空间。在这种情况下,您可以使用FileSystemResource