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

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

323844 次浏览

选项1 使用 < a href = “ http://docs.spring.io/spring-Framework/docs/current/javadoc-api/org/springFramework/core/io/InputStreamResource.html”rel = “ noReferrer”> InputStreamResource

资源 给定 InputStream的实现。

只有在没有其他特定的 Resource 实现 > 适用的情况下才应该使用。特别是,在可能的情况下,更喜欢 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);
}

正如 InputStreamResource 的文档所建议的那样,选项2 使用 字节数组资源:

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

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

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


}

我想分享一个用 JavaScript (ES6)、 做出反应弹簧靴后端下载文件的简单方法:

  1. Spring boot Rest Controller

来自 Org.springframework.core.io 资源的资源

    @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. 使用 AXIOS < br > 响应 API 调用

将 response seType 设置为 < strong > arraybuffer 以指定包含在响应中的数据类型。

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

最后一步 > 下载
Js-file-download的帮助下,你可以触发浏览器将数据保存到文件中,就好像它是下载的一样。

DownloadFile('filename.extension', 'extraVariable').then(
(response) => {
fileDownload(response.data, filename);
}
, (error) => {
// ERROR
});
    @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);
}

为了测试它,使用邮递员

Http://localhost:8080/api/downloadfile/gdd/1.zip

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

附注: 在使用 StreamingResponseBody 流媒体响应机构时,强烈建议配置 SpringMVC 中用于执行异步请求的 TaskExecator。TaskExecator 是一个抽象 Runnable 执行的接口。

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

如果需要从服务器的文件系统下载大型文件,那么 字节数组资源可以占用所有 Java 堆空间。在这种情况下,您可以使用 FileSystemResource 文件系统资源

使用 Apache IO 可能是复制 Stream 的另一种选择

@RequestMapping(path = "/file/{fileId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> downloadFile(@PathVariable(value="fileId") String fileId,HttpServletResponse response) throws Exception {


InputStream yourInputStream = ...
IOUtils.copy(yourInputStream, response.getOutputStream());
response.flushBuffer();
return ResponseEntity.ok().build();
}

专家依赖

    <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>