Spring MVC文件下载控制器返回乱码内容

时间:2014-11-05 00:35:07

标签: spring-mvc download

我正在尝试编写一个控制器来返回要下载的文件。

起初我用这种方式编码:

@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/家庭/斯特凡的/ tmp / language.csv%E1 ÀÐÝÃô™uìÐHßT‰±ØûC....oy2Èy€l ¨ V8EWc /Ïñܪμ÷&GT;ú£¡.J <] @uåtmáò×PKC}:HOPKîeE/家/斯蒂芬/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回应。

从上次的上一次尝试中,我删除了属性:produce =

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"])

4 个答案:

答案 0 :(得分:0)

您需要在控制器中设置正确的MIME类型。

如果您的文件是pdf:

response.setContentType("application/pdf");

如果您的文件是gif图片:

response.setContentType("image/gif");

如果您的文件是zip文件:

response.setContentType("application/zip");

您可能会找到正确的MIME类型here

答案 1 :(得分:0)

您应该设置HttpResponse的内容类型,如下所示:

response.setContentType("application/zip");

或使用ContentType设置HttpHeaders,并将CONTENT_ENCODING设置为UTF-8,如下所示:

HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set(HttpHeaders.CONTENT_TYPE, "application/zip");
responseHeaders.add(HttpHeaders.CONTENT_ENCODING, "UTF-8");

所以浏览器会知道他期待什么样的文件,请参阅本教程here,它解释了Http Headers。

希望有助于。

答案 2 :(得分:0)

我可以使用这个控制器:

@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
浏览器中的

会弹出一个下载窗口。

答案 3 :(得分:0)

删除

produces = MediaType.APPLICATION_OCTET_STREAM_VALUE

将推出通用二进制内容。如果你想从文件扩展名发送的内容然后在你的spring XML文件中放入

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean" />