我有个要求,我需要从网站上下载一份PDF。PDF需要在代码中生成,我认为这将是freemarker和像iText这样的PDF生成框架的组合。还有更好的办法吗?
然而,我的主要问题是如何允许用户通过Spring控制器下载文件?
我有个要求,我需要从网站上下载一份PDF。PDF需要在代码中生成,我认为这将是freemarker和像iText这样的PDF生成框架的组合。还有更好的办法吗?
然而,我的主要问题是如何允许用户通过Spring控制器下载文件?
当前回答
Do
从处理程序方法返回ResponseEntity<Resource> 指定内容类型 如果需要,设置Content-Disposition: 文件名 类型 内联以强制在浏览器中预览 附件强制下载
例子
@Controller
public class DownloadController {
@GetMapping("/downloadPdf.pdf")
// 1.
public ResponseEntity<Resource> downloadPdf() {
FileSystemResource resource = new FileSystemResource("/home/caco3/Downloads/JMC_Tutorial.pdf");
// 2.
MediaType mediaType = MediaTypeFactory
.getMediaType(resource)
.orElse(MediaType.APPLICATION_OCTET_STREAM);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
// 3
ContentDisposition disposition = ContentDisposition
// 3.2
.inline() // or .attachment()
// 3.1
.filename(resource.getFilename())
.build();
headers.setContentDisposition(disposition);
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}
}
解释
返回ResponseEntity <资源>
当您返回ResponseEntity<Resource>时,ResourceHttpMessageConverter将写入文件内容
资源实现的例子:
基于字节的ByteArrayResource [] FileSystemResource—用于文件或路径 UrlResource -从java.net.URL检索 GridFsResource—存储在MongoDB中的blob ClassPathResource—用于类路径中的文件,例如资源目录中的文件。我对“从Spring Boot中的资源文件夹中读取文件”问题的回答详细解释了如何在类路径中定位资源
显式指定Content-Type:
原因:请参阅“FileSystemResource返回内容类型为json”问题
选项:
硬编码头文件 使用Spring中的MediaTypeFactory。MediaTypeFactory使用/org/springframework/http/mime将Resource映射到MediaType。类型文件 使用第三方库,如Apache Tika
如果需要,设置Content-Disposition:
关于Content-Disposition头:
HTTP上下文中的第一个参数要么是内联(默认值,表明它可以在Web页面中显示,也可以作为Web页面显示),要么是附件(表明应该下载它;大多数浏览器会显示一个“另存为”对话框,如果存在的话,会预先填充文件名参数的值)。
在应用中使用ContentDisposition:
在浏览器中预览文件: 性格=性格 .inline () .filename (resource.getFilename ()) .build (); 强制下载: 性格=性格 .attachment () .filename (resource.getFilename ()) .build ();
小心使用InputStreamResource:
使用HttpHeaders#setContentLength方法指定Content-Length,如果:
长度是已知的 使用InputStreamResource
原因:Spring不会为InputStreamResource写Content-Length,因为Spring不能确定资源的长度。下面是来自ResourceHttpMessageConverter的一段代码:
@Override
protected Long getContentLength(Resource resource, @Nullable MediaType contentType) throws IOException {
// Don't try to determine contentLength on InputStreamResource - cannot be read afterwards...
// Note: custom InputStreamResource subclasses could provide a pre-calculated content length!
if (InputStreamResource.class == resource.getClass()) {
return null;
}
long contentLength = resource.contentLength();
return (contentLength < 0 ? null : contentLength);
}
在其他情况下,Spring设置Content-Length:
~ $ curl -I localhost:8080/downloadPdf.pdf | grep "Content-Length"
Content-Length: 7554270
其他回答
我能很快想到的是,生成pdf并将其存储在webapp/downloads/< RANDOM-FILENAME>.pdf代码中,并使用HttpServletRequest发送一个转发到这个文件
request.getRequestDispatcher("/downloads/<RANDOM-FILENAME>.pdf").forward(request, response);
或者你可以这样配置你的视图解析器,
<bean id="pdfViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="order" value=”2″/>
<property name="prefix" value="/downloads/" />
<property name="suffix" value=".pdf" />
</bean>
然后返回
return "RANDOM-FILENAME";
下面的解决方案对我很有效
@RequestMapping(value="/download")
public void getLogFile(HttpSession session,HttpServletResponse response) throws Exception {
try {
String fileName="archivo demo.pdf";
String filePathToBeServed = "C:\\software\\Tomcat 7.0\\tmpFiles\\";
File fileToDownload = new File(filePathToBeServed+fileName);
InputStream inputStream = new FileInputStream(fileToDownload);
response.setContentType("application/force-download");
response.setHeader("Content-Disposition", "attachment; filename="+fileName);
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
inputStream.close();
} catch (Exception exception){
System.out.println(exception.getMessage());
}
}
在Spring 3.0中,您可以使用HttpEntity返回对象。如果您使用它,那么您的控制器就不需要HttpServletResponse对象,因此它更容易测试。 除了这个,这个答案是相对等于Infeligo的答案。
如果你的pdf框架的返回值是一个字节数组(阅读我回答的其他返回值的第二部分):
@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
@PathVariable("fileName") String fileName) throws IOException {
byte[] documentBody = this.pdfFramework.createPdf(filename);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_PDF);
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName.replace(" ", "_"));
header.setContentLength(documentBody.length);
return new HttpEntity<byte[]>(documentBody, header);
}
如果你的PDF框架(documentBbody)的返回类型不是一个字节数组(也没有ByteArrayInputStream),那么它将是明智的不让它成为一个字节数组。相反,最好使用:
InputStreamResource, PathResource(从Spring 4.0开始)或 FileSystemResource,
使用FileSystemResource示例:
@RequestMapping(value = "/files/{fileName}", method = RequestMethod.GET)
public HttpEntity<byte[]> createPdf(
@PathVariable("fileName") String fileName) throws IOException {
File document = this.pdfFramework.createPdf(filename);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_PDF);
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName.replace(" ", "_"));
header.setContentLength(document.length());
return new HttpEntity<byte[]>(new FileSystemResource(document),
header);
}
我能够通过使用Spring的ResourceHttpMessageConverter中的内置支持来进行流处理。如果可以确定mime类型,这将设置内容长度和内容类型
@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
return new FileSystemResource(myService.getFileFor(fileName));
}
我必须加上这个才能下载任何文件
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment;filename="+"file.txt");
所有的代码:
@Controller
public class FileController {
@RequestMapping(value = "/file", method =RequestMethod.GET)
@ResponseBody
public FileSystemResource getFile(HttpServletResponse response) {
final File file = new File("file.txt");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment;filename="+"file.txt");
return new FileSystemResource(file);
}
}