Java 从 Spring boot rest 服务下载文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35680932/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 17:00:23  来源:igfitidea点击:

download a file from Spring boot rest service

javaspringrest

提问by kiran

I am trying to download a file from a Spring boot rest service.

我正在尝试从 Spring boot rest 服务下载文件。

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

When I tried to download the file from the browser, it starts the download, but always fails. Is there anything wrong with the service which is causing the download to fail?

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

采纳答案by fateddy

Option 1using an InputStreamResource

使用InputStreamResource 的选项 1

Resourceimplementation for a given InputStream.

Should only be used if no other specific Resource implementation is > applicable. In particular, prefer ByteArrayResource or any of the file-based Resource implementations where possible.

给定InputStream 的资源实现。

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

@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.APPLICATION_OCTET_STREAM)
            .body(resource);
}


Option2as the documentation of the InputStreamResource suggests - using a ByteArrayResource:

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.APPLICATION_OCTET_STREAM)
            .body(resource);
}

回答by Rajesh D

The below Sample code worked for me and might help someone.

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

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

}

回答by fetahokey

I want to share a simple approach for downloading files with JavaScript (ES6), Reactand a Spring Bootbackend:

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

  1. Spring boot Rest Controller
  1. Spring Boot Rest 控制器

Resource from org.springframework.core.io.Resource

来自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);
    }
  1. React, API call using AXIOS
  1. React,使用AXIOS 的API 调用

Set the responseType to arraybufferto specify the type of data contained in the response.

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

Final step > downloading
with the help of js-file-downloadyou can trigger browser to save data to file as if it was downloaded.

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

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

回答by Feng Zhang

    @GetMapping("/downloadfile/{productId}/{fileName}")
public ResponseEntity<Resource> downloadFile(@PathVariable(value = "productId") String productId,
        @PathVariable String fileName, HttpServletRequest request) {
    // Load file as Resource
    Resource resource;

    String fileBasePath = "C:\Users\v_fzhang\mobileid\src\main\resources\data\Filesdown\" + productId
            + "\";
    Path path = Paths.get(fileBasePath + fileName);
    try {
        resource = new UrlResource(path.toUri());
    } catch (MalformedURLException e) {
        e.printStackTrace();
        return null;
    }

    // Try to determine file's content type
    String contentType = null;
    try {
        contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
    } catch (IOException ex) {
        System.out.println("Could not determine file type.");
    }

    // Fallback to the default content type if type could not be determined
    if (contentType == null) {
        contentType = "application/octet-stream";
    }

    return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType))
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
            .body(resource);
}

To test it, use postman

要测试它,请使用邮递员

http://localhost:8080/api/downloadfile/GDD/1.zip

http://localhost:8080/api/downloadfile/GDD/1.zip

回答by Felipe Desiderati

I would suggest using a StreamingResponseBodysince with it the application can write directly to the response (OutputStream) without holding up the Servlet container thread. It is a good approach if you are downloading a file very large.

我建议使用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);
        }
    };
}

Ps.: When using StreamingResponseBody, it is highly recommended to configure TaskExecutor used in Spring MVC for executing asynchronous requests. TaskExecutor is an interface that abstracts the execution of a Runnable.

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

More info: https://medium.com/swlh/streaming-data-with-spring-boot-restful-web-service-87522511c071

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

回答by Taras Melon

If you need to download a huge file from the server's file system, then ByteArrayResourcecan take all Java heap space. In that case, you can use FileSystemResource

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