标签:package gets serial sea input rop deb auth client
spring3以后添加httpMessageConverter消怎机制。其中可以通过org.springframework.http.ResponseEntity<byte[]>对象下载文件。
pom文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.14.RELEASE</version>
</parent>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 这个是剔除掉自带的 tomcat部署的 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- tomcat容器部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- 支持 @ConfigurationProperties 注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>mainMaven</id>
<name>aliyunMaven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<packaging>war</packaging>
</project>
引用额外jar包 common-io.jar
下载文件代码:
RequestMapping("/filedownload")
public org.springframework.http.ResponseEntity<byte[]> filedownload() throws UnsupportedEncodingException,IOException {
File file = new File("文件路径");
String fileName=new String("文件名".getBytes("UTF-8"),"iso-8859-1");
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename="+fileName);
return new org.springframework.http.ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
}
文件原内容:
{"settings":{"analysis":{"analyzer":{"letterAnalyzer":{"type":"custom","tokenizer":"letter"}}}}, "mappings":{"my_type":{"properties":{"title": {"type":"string","analyzer":"letterAnalyzer","search_analyzer":"letterAnalyzer", "search_quote_analyzer":"letterAnalyzer"}}}}}}
下件后文件内容:
"eyJzZXR0aW5ncyI6eyJhbmFseXNpcyI6eyJhbmFseXplciI6eyJsZXR0ZXJBbmFseXplciI6eyJ0eXBlIjoiY3VzdG9tIiwidG9rZW5pemVyIjoibGV0dGVyIn19fX0sICJtYXBwaW5ncyI6eyJteV90eXBlIjp7InByb3BlcnRpZXMiOnsidGl0bGUiOiB7InR5cGUiOiJzdHJpbmciLCJhbmFseXplciI6ImxldHRlckFuYWx5emVyIiwic2VhcmNoX2FuYWx5emVyIjoibGV0dGVyQW5hbHl6ZXIiLCAic2VhcmNoX3F1b3RlX2FuYWx5emVyIjoibGV0dGVyQW5hbHl6ZXIifX19fX19"
文件内容全是乱码,百度了很久没什么结果,只知道是跟HttpMessageConvert有关系。
打开Spring源码找到AbstractMessageConverterMethodProcessor处理类的执行方法writeWithMessageConverters内容如下:
@SuppressWarnings("unchecked") protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object outputValue; Class<?> valueType; Type declaredType; if (value instanceof CharSequence) { outputValue = value.toString(); valueType = String.class; declaredType = String.class; } else { outputValue = value; valueType = getReturnValueType(outputValue, returnType); declaredType = getGenericType(returnType); } HttpServletRequest request = inputMessage.getServletRequest(); List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request); List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType); if (outputValue != null && producibleMediaTypes.isEmpty()) { throw new IllegalArgumentException("No converter found for return value of type: " + valueType); } Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>(); for (MediaType requestedType : requestedMediaTypes) { for (MediaType producibleType : producibleMediaTypes) { if (requestedType.isCompatibleWith(producibleType)) { compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType)); } } } if (compatibleMediaTypes.isEmpty()) { if (outputValue != null) { throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes); } return; } List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes); MediaType.sortBySpecificityAndQuality(mediaTypes); MediaType selectedMediaType = null; for (MediaType mediaType : mediaTypes) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter<?> messageConverter : this.messageConverters) { if (messageConverter instanceof GenericHttpMessageConverter) { if (((GenericHttpMessageConverter) messageConverter).canWrite( declaredType, valueType, selectedMediaType)) { outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage); if (outputValue != null) { addContentDispositionHeader(inputMessage, outputMessage); ((GenericHttpMessageConverter) messageConverter).write( outputValue, declaredType, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]"); } } return; } } else if (messageConverter.canWrite(valueType, selectedMediaType)) { outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage); if (outputValue != null) { addContentDispositionHeader(inputMessage, outputMessage); ((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]"); } } return; } } } if (outputValue != null) { throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } }
重点是这行代码messageConverter instanceof GenericHttpMessageConverter和这行messageConverter.canWrite(valueType, selectedMediaType
HttpMessageConverter一般都继承AbstractHttpMessageConverter类,但有的HttpMessageConverter还实现了GenericHttpMessageConverter<Object>接口。
如果实现了GenericHttpMessageConverter接口就是通用消息转换类,所有类型的消息都会尝试调用GenericHttpMessageConverter.canWrite方法。没有实现的则调用
AbstractHttpMessageConverter的canWrite方法。如果canWrite返回true。则调用HttpMessageConverter的write方法。
调试代码,调试结果是FastJsonHttpMessageConvertery这个类转换了返回消息。
我的项目里使用了alibaba的FastJson,FastJson的HttpMessageConterver实现类是FastJsonHttpMessageConverter实现了GenericHttpMessageConverter<Object>接口
package com.alibaba.fastjson.support.spring; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONPObject; import com.alibaba.fastjson.serializer.SerializeFilter; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.util.IOUtils; import org.springframework.core.ResolvableType; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Fastjson for Spring MVC Converter. * <p> * Compatible Spring MVC version 3.2+ * * @author VictorZeng * @see AbstractHttpMessageConverter * @see GenericHttpMessageConverter * @since 1.2.10 * <p> * <p> * <p> * Supported return type: * </p> * Simple object: Object * <p> * <p> * With property filter :FastJsonContainer[Object] * </p> * <p> * Jsonp :MappingFastJsonValue[Object] * </p> * Jsonp with property filter: MappingFastJsonValue[FastJsonContainer[Object]] */ public class FastJsonHttpMessageConverter extends AbstractHttpMessageConverter<Object>// implements GenericHttpMessageConverter<Object> { public static final MediaType APPLICATION_JAVASCRIPT = new MediaType("application", "javascript"); private Charset charset = Charset.forName("UTF-8"); @Deprecated protected SerializerFeature[] features = new SerializerFeature[0]; @Deprecated protected SerializeFilter[] filters = new SerializeFilter[0]; @Deprecated protected String dateFormat; /** * with fastJson config */ private FastJsonConfig fastJsonConfig = new FastJsonConfig(); /** * @return the fastJsonConfig. * @since 1.2.11 */ public FastJsonConfig getFastJsonConfig() { return fastJsonConfig; } /** * @param fastJsonConfig the fastJsonConfig to set. * @since 1.2.11 */ public void setFastJsonConfig(FastJsonConfig fastJsonConfig) { this.fastJsonConfig = fastJsonConfig; } /** * Can serialize/deserialize all types. */ public FastJsonHttpMessageConverter() { super(MediaType.ALL); } @Deprecated public Charset getCharset() { return this.fastJsonConfig.getCharset(); } @Deprecated public void setCharset(Charset charset) { this.fastJsonConfig.setCharset(charset); } @Deprecated public String getDateFormat() { return this.fastJsonConfig.getDateFormat(); } @Deprecated public void setDateFormat(String dateFormat) { this.fastJsonConfig.setDateFormat(dateFormat); } @Deprecated public SerializerFeature[] getFeatures() { return this.fastJsonConfig.getSerializerFeatures(); } @Deprecated public void setFeatures(SerializerFeature... features) { this.fastJsonConfig.setSerializerFeatures(features); } @Deprecated public SerializeFilter[] getFilters() { return this.fastJsonConfig.getSerializeFilters(); } @Deprecated public void setFilters(SerializeFilter... filters) { this.fastJsonConfig.setSerializeFilters(filters); } @Deprecated public void addSerializeFilter(SerializeFilter filter) { if (filter == null) { return; } int length = this.fastJsonConfig.getSerializeFilters().length; SerializeFilter[] filters = new SerializeFilter[length + 1]; System.arraycopy(this.fastJsonConfig.getSerializeFilters(), 0, filters, 0, length); filters[filters.length - 1] = filter; this.fastJsonConfig.setSerializeFilters(filters); } @Override protected boolean supports(Class<?> clazz) { return true; } public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) { return super.canRead(contextClass, mediaType); } public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) { return super.canWrite(clazz, mediaType); } /* * @see org.springframework.http.converter.GenericHttpMessageConverter#read(java.lang.reflect.Type, java.lang.Class, org.springframework.http.HttpInputMessage) */ public Object read(Type type, // Class<?> contextClass, // HttpInputMessage inputMessage // ) throws IOException, HttpMessageNotReadableException { return readType(getType(type, contextClass), inputMessage); } /* * @see org.springframework.http.converter.GenericHttpMessageConverter.write */ public void write(Object o, Type type, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { super.write(o, contentType, outputMessage);// support StreamingHttpOutputMessage in spring4.0+ //writeInternal(o, outputMessage); } /* * @see org.springframework.http.converter.AbstractHttpMessageConverter#readInternal(java.lang.Class, org.springframework.http.HttpInputMessage) */ @Override protected Object readInternal(Class<? extends Object> clazz, // HttpInputMessage inputMessage // ) throws IOException, HttpMessageNotReadableException { return readType(getType(clazz, null), inputMessage); } private Object readType(Type type, HttpInputMessage inputMessage) throws IOException { try { InputStream in = inputMessage.getBody(); return JSON.parseObject(in, fastJsonConfig.getCharset(), type, fastJsonConfig.getFeatures()); } catch (JSONException ex) { throw new HttpMessageNotReadableException("JSON parse error: " + ex.getMessage(), ex); } catch (IOException ex) { throw new HttpMessageNotReadableException("I/O error while reading input message", ex); } } @Override protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { ByteArrayOutputStream outnew = new ByteArrayOutputStream(); try { HttpHeaders headers = outputMessage.getHeaders(); //鑾峰彇鍏ㄥ眬閰嶇疆鐨刦ilter SerializeFilter[] globalFilters = fastJsonConfig.getSerializeFilters(); List<SerializeFilter> allFilters = new ArrayList<SerializeFilter>(Arrays.asList(globalFilters)); boolean isJsonp = false; //涓嶇煡閬撲负浠?涔堜細鏈夎繖琛屼唬鐮侊紝 浣嗘槸涓轰簡淇濇寔鍜屽師鏉ョ殑琛屼负涓?鑷达紝杩樻槸淇濈暀涓嬫潵 Object value = strangeCodeForJackson(object); if (value instanceof FastJsonContainer) { FastJsonContainer fastJsonContainer = (FastJsonContainer) value; PropertyPreFilters filters = fastJsonContainer.getFilters(); allFilters.addAll(filters.getFilters()); value = fastJsonContainer.getValue(); } //revise 2017-10-23 , // 淇濇寔鍘熸湁鐨凪appingFastJsonValue瀵硅薄鐨刢ontentType涓嶅仛淇敼 淇濇寔鏃х増鍏煎銆? // 浣嗘槸鏂扮殑JSONPObject灏嗚繑鍥炴爣鍑嗙殑contentType锛歛pplication/javascript 锛屼笉瀵规槸鍚︽湁function杩涜鍒ゆ柇 if (value instanceof MappingFastJsonValue) { if(!StringUtils.isEmpty(((MappingFastJsonValue) value).getJsonpFunction())){ isJsonp = true; } } else if (value instanceof JSONPObject) { isJsonp = true; } int len = JSON.writeJSONString(outnew, // fastJsonConfig.getCharset(), // value, // fastJsonConfig.getSerializeConfig(), // //fastJsonConfig.getSerializeFilters(), // allFilters.toArray(new SerializeFilter[allFilters.size()]), fastJsonConfig.getDateFormat(), // JSON.DEFAULT_GENERATE_FEATURE, // fastJsonConfig.getSerializerFeatures()); if (isJsonp) { headers.setContentType(APPLICATION_JAVASCRIPT); } if (fastJsonConfig.isWriteContentLength()) { headers.setContentLength(len); } outnew.writeTo(outputMessage.getBody()); } catch (JSONException ex) { throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex); } finally { outnew.close(); } } private Object strangeCodeForJackson(Object obj) { if (obj != null) { String className = obj.getClass().getName(); if ("com.fasterxml.jackson.databind.node.ObjectNode".equals(className)) { return obj.toString(); } } return obj; } protected Type getType(Type type, Class<?> contextClass) { if (Spring4TypeResolvableHelper.isSupport()) { return Spring4TypeResolvableHelper.getType(type, contextClass); } return type; } private static class Spring4TypeResolvableHelper { private static boolean hasClazzResolvableType; static { try { Class.forName("org.springframework.core.ResolvableType"); hasClazzResolvableType = true; } catch (ClassNotFoundException e) { hasClazzResolvableType = false; } } private static boolean isSupport() { return hasClazzResolvableType; } private static Type getType(Type type, Class<?> contextClass) { if (contextClass != null) { ResolvableType resolvedType = ResolvableType.forType(type); if (type instanceof TypeVariable) { ResolvableType resolvedTypeVariable = resolveVariable((TypeVariable) type, ResolvableType.forClass(contextClass)); if (resolvedTypeVariable != ResolvableType.NONE) { return resolvedTypeVariable.resolve(); } } else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) { ParameterizedType parameterizedType = (ParameterizedType) type; Class<?>[] generics = new Class[parameterizedType.getActualTypeArguments().length]; Type[] typeArguments = parameterizedType.getActualTypeArguments(); for (int i = 0; i < typeArguments.length; ++i) { Type typeArgument = typeArguments[i]; if (typeArgument instanceof TypeVariable) { ResolvableType resolvedTypeArgument = resolveVariable((TypeVariable) typeArgument, ResolvableType.forClass(contextClass)); if (resolvedTypeArgument != ResolvableType.NONE) { generics[i] = resolvedTypeArgument.resolve(); } else { generics[i] = ResolvableType.forType(typeArgument).resolve(); } } else { generics[i] = ResolvableType.forType(typeArgument).resolve(); } } return ResolvableType.forClassWithGenerics(resolvedType.getRawClass(), generics).getType(); } } return type; } private static ResolvableType resolveVariable(TypeVariable<?> typeVariable, ResolvableType contextType) { ResolvableType resolvedType; if (contextType.hasGenerics()) { resolvedType = ResolvableType.forType(typeVariable, contextType); if (resolvedType.resolve() != null) { return resolvedType; } } ResolvableType superType = contextType.getSuperType(); if (superType != ResolvableType.NONE) { resolvedType = resolveVariable(typeVariable, superType); if (resolvedType.resolve() != null) { return resolvedType; } } for (ResolvableType ifc : contextType.getInterfaces()) { resolvedType = resolveVariable(typeVariable, ifc); if (resolvedType.resolve() != null) { return resolvedType; } } return ResolvableType.NONE; } } }
查看canWrite方法调用了AbstractHttpMessageConverter的canWrite方法:
@Override public boolean canWrite(Class<?> clazz, MediaType mediaType) { return supports(clazz) && canWrite(mediaType); }
AbstractHttpMessageConvertercanWrite方法里有支持supports方法
@Override protected boolean supports(Class<?> clazz) { return true; }
FastJsonHttpMessageConverter里的实现是直接返回true.
这下原因找到了byte[]类型的消息由FastJsonHttpMessageConverter转换了所以内容乱码。
解决方法重写FastJsonHttpMessageConverter排除对byte[]类型的支持。代码如下。
public class FastJsonHttpMessageConverterImpl extends FastJsonHttpMessageConverter { @Override protected boolean supports(Class<?> clazz) { if (clazz.equals(byte[].class)) { return false; } return true; } }
springboot ResponseEntity<byte[]> 下载文件乱码
标签:package gets serial sea input rop deb auth client
原文地址:https://www.cnblogs.com/xtly2012/p/9759641.html