我正在尝试编写一个控制器来返回文件以供下载。
起初我是这样编码的:
@RequestMapping(value = RESTConstants.SLASH + "{id}" + RESTConstants.SLASH + RESTConstants.EXPORT, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public ResponseEntity<FileSystemResource> export(@PathVariable Long id, UriComponentsBuilder builder) throws IOException {
String filename = rolloutExportService.getDownloadFilename();
FileSystemResource fileSystemResource = rolloutExportService.export(id);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
int read = 0;
byte[] bytes = new byte[1024];
OutputStream outputStream = null;
InputStream inputStream = null;
ResponseEntity<FileSystemResource> responseEntity = new ResponseEntity<FileSystemResource>(fileSystemResource, responseHeaders, HttpStatus.OK);
try {
responseHeaders.add(HttpHeaders.CONTENT_LENGTH, Long.toString(fileSystemResource.contentLength()));
outputStream = responseEntity.getBody().getOutputStream();
inputStream = fileSystemResource.getInputStream();
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
} finally {
inputStream.close();
outputStream.flush();
outputStream.close();
}
return responseEntity;
}
但是响应将是 406 此请求标识的资源只能根据请求"accept"标头生成具有不可接受特征的响应。
控制台日志显示:
2014-11-05 01:18:39,403 DEBUG [DefaultHandlerExceptionResolver] Resolving exception from handler [public org.springframework.http.ResponseEntity<org.springframework.core.io.FileSystemResource> com.nsn.nitro.project.rest.controller.RolloutExportController.export(java.lang.Long,org.springframework.web.util.UriComponentsBuilder) throws java.io.IOException]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2014-11-05 01:18:39,403 DEBUG [DispatcherServlet] Null ModelAndView returned to DispatcherServlet with name 'NITRo': assuming HandlerAdapter completed request handling
2014-11-05 01:18:39,403 DEBUG [DispatcherServlet] Successfully completed request
2014-11-05 01:18:39,405 DEBUG [ExceptionTranslationFilter] Chain processed normally
2014-11-05 01:18:39,405 DEBUG [SecurityContextPersistenceFilter] SecurityContextHolder now cleared, as request processing completed
所以我更改了它以不返回如下响应:
@RequestMapping(value = RESTConstants.SLASH + "{id}" + RESTConstants.SLASH + RESTConstants.EXPORT, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public void export(@PathVariable Long id, HttpServletResponse response) throws IOException {
String filename = rolloutExportService.getDownloadFilename();
FileSystemResource fileSystemResource = rolloutExportService.export(id);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
int read = 0;
byte[] bytes = new byte[1024];
OutputStream outputStream = null;
InputStream inputStream = null;
try {
responseHeaders.add(HttpHeaders.CONTENT_LENGTH, Long.toString(fileSystemResource.contentLength()));
outputStream = response.getOutputStream();
inputStream = fileSystemResource.getInputStream();
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
} finally {
inputStream.close();
outputStream.flush();
outputStream.close();
}
}
在两个控制器上,请求是相同的,在 Chromium 浏览器中键入:
http://localhost:8080/nitro-project-rest/rollouts/3/export
但响应将是浏览器中显示的乱码内容:PK îeE/home/stephane/tmp/language.csv%É1À ÐÝÃô™uìÐHˆßT‰±ØûC....oy2Èy€l¨V8EWc/Ïñܪμ÷>ú£¡.J‹]@uåtmáò×PKç}:H O PK îeE/home/stephane/tmp/country.csvKÎOIμÎKÌMμÎÉ,.ñ/JI-âJͳvÍKÏIÌK±6äJ+²v+JÌKN²PK*9»¶- - PK îeE/home/stephane/tmp/team.csvËKÌMμNI-N.Ê,(ÉÌϳN.JM1\KRK2sS¹BRs¬C2RJ
控制台说:
20. select team0_.id as id1_15_0_, team0_.version as version2_15_0_, team0_.creation_datetime as
creation3_15_0_, team0_.description as descript4_15_0_, team0_.name as name5_15_0_ from team
team0_ where team0_.id=2
2014-11-05 01:31:29,983 DEBUG [JpaTransactionManager] Initiating transaction commit
2014-11-05 01:31:29,983 DEBUG [JpaTransactionManager] Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6569cc]
2014-11-05 01:31:29,988 DEBUG [JpaTransactionManager] Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6569cc] after transaction
2014-11-05 01:31:29,988 DEBUG [EntityManagerFactoryUtils] Closing JPA EntityManager
2014-11-05 01:31:30,013 DEBUG [DispatcherServlet] Null ModelAndView returned to DispatcherServlet with name 'NITRo': assuming HandlerAdapter completed request handling
2014-11-05 01:31:30,013 DEBUG [DispatcherServlet] Successfully completed request
2014-11-05 01:31:30,021 DEBUG [ExceptionTranslationFilter] Chain processed normally
2014-11-05 01:31:30,021 DEBUG [SecurityContextPersistenceFilter] SecurityContextHolder now cleared, as request processing completed
我的最后一次尝试是这样的:
@RequestMapping(value = RESTConstants.SLASH + "{id}" + RESTConstants.SLASH + RESTConstants.EXPORT, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public FileSystemResource export(@PathVariable Long id) throws IOException {
String filename = rolloutExportService.getDownloadFilename();
FileSystemResource fileSystemResource = rolloutExportService.export(id);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
responseHeaders.add(HttpHeaders.CONTENT_LENGTH, Long.toString(fileSystemResource.contentLength()));
responseHeaders.add(HttpHeaders.CONTENT_TYPE, "application/zip");
return fileSystemResource;
}
但它也给了我一个406的回应。
从上面的最后一次尝试中,我删除了属性:产生 =
MediaType.APPLICATION_OCTET_STREAM_VALUE
浏览器发送的相同请求(在控制台中看到(如下所示:
Request URL:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAQAAAD9sOO8AAAASUlEQVR4Xu3SMQoAIAzFUO/o/c+iY4fSSfgQyesgOESHrs9I0j7TEOL9pk5i/OmB/s9IvAYQH6DjxmtTWPEWBcS52zJPIJ4lSRfUZeB1c/cgSwAAAABJRU5ErkJggg==
Request Headers CAUTION: Provisional headers are shown.
User-Agent:Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/34.0.1847.116 Chrome/34.0.1847.116 Safari/537.36
服务器控制台日志显示:
2014-11-05 12:08:35,938 DEBUG [ExceptionsHandler] org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.core.io.InputStreamResource["inputStream"]->java.io.FileInputStream["fd"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.core.io.InputStreamResource["inputStream"]->java.io.FileInputStream["fd"])
您需要在控制器中设置文件的正确 MIME 类型。
如果您的文件是 pdf:
response.setContentType("application/pdf");
如果您的文件是 gif 图像:
response.setContentType("image/gif");
如果您的文件是 zip 文件:
response.setContentType("application/zip");
您可以在此处找到正确的 MIME 类型。
您应该像下面这样设置 HttpResponse 的内容类型:
response.setContentType("application/zip");
或使用 HttpHeaders 设置 ContentType,并将CONTENT_ENCODING设置为 UTF-8,如下所示:
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set(HttpHeaders.CONTENT_TYPE, "application/zip");
responseHeaders.add(HttpHeaders.CONTENT_ENCODING, "UTF-8");
所以浏览器会知道他期待什么样的文件,请参考本教程 这里解释了 Http 标头 .
希望有帮助.
我可以让它与这个控制器一起工作:
@RequestMapping(value = RESTConstants.SLASH + "{id}" + RESTConstants.SLASH + RESTConstants.EXPORT, method = RequestMethod.GET)
public void doDownload(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) throws IOException {
String filename = rolloutExportService.export(id);
ServletContext context = request.getServletContext();
File downloadFile = new File(filename);
FileInputStream inputStream = new FileInputStream(downloadFile);
String mimeType = context.getMimeType(filename);
if (mimeType == null) {
mimeType = MediaType.APPLICATION_OCTET_STREAM.getType();
}
response.setContentType(mimeType);
response.setContentLength((int) downloadFile.length());
String headerValue = String.format("attachment; filename="%s"", downloadFile.getName());
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, headerValue);
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outStream.close();
}
发送请求
http://localhost:8080/nitro-project-rest/rollouts/3/expor
在浏览器中弹出一个下载窗口。
删除
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
将推出通用二进制内容。如果您希望从文件扩展名发送内容,请在您的春季XML文件中放置
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
和
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean" />