spring3以后添加httpMessageConverter消怎机制。其中可以通过org.springframework.http.ResponseEntity对象下载文件。
pom文件如下:
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"> 4.0.0
org.springframework.boot spring-boot-starter-parent 1.5.14.RELEASE
test test 0.0.1-SNAPSHOT
org.springframework.boot spring-boot-starter
org.springframework.boot spring-boot-starter-test test
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat
org.springframework.boot spring-boot-starter-tomcat provided
org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.0
com.github.pagehelper pagehelper-spring-boot-starter 1.2.5
org.springframework.boot spring-boot-devtools true
org.springframework.boot spring-boot-configuration-processor true org.apache.httpcomponents httpclient
com.alibaba fastjson 1.2.47
org.apache.commons commons-lang3 3.0
org.apache.commons commons-dbcp2
org.apache.commons commons-pool2
org.springframework.boot spring-boot-starter-jdbc
mysql mysql-connector-java
com.belerweb pinyin4j 2.5.1
commons-beanutils commons-beanutils
src/main org.apache.maven.plugins maven-compiler-plugin 1.7 1.7 org.springframework.boot spring-boot-maven-plugin repackage org.apache.maven.plugins maven-war-plugin false
mainMaven aliyunMaven http://maven.aliyun.com/nexus/content/groups/public/ war
引用额外jar包 common-io.jar
下载文件代码:
RequestMapping("/filedownload") public org.springframework.http.ResponseEntity 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(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 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 requestedMediaTypes = getAcceptableMediaTypes(request);
List producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
if (outputValue != null && producibleMediaTypes.isEmpty()) {
throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
}
Set compatibleMediaTypes = new LinkedHashSet();
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 mediaTypes = new ArrayList(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接口。
如果实现了GenericHttpMessageConverter接口就是通用消息转换类,所有类型的消息都会尝试调用GenericHttpMessageConverter.canWrite方法。没有实现的则调用
AbstractHttpMessageConverter的canWrite方法。如果canWrite返回true。则调用HttpMessageConverter的write方法。
调试代码,调试结果是FastJsonHttpMessageConvertery这个类转换了返回消息。
我的项目里使用了alibaba的FastJson,FastJson的HttpMessageConterver实现类是FastJsonHttpMessageConverter实现了GenericHttpMessageConverter接口
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.
*
* Compatible Spring MVC version 3.2+
*
* @author VictorZeng
* @see AbstractHttpMessageConverter
* @see GenericHttpMessageConverter
* @since 1.2.10
*
*
*
* Supported return type:
*
* Simple object: Object
*
*
* With property filter :FastJsonContainer[Object]
*
*
* Jsonp :MappingFastJsonValue[Object]
*
* Jsonp with property filter: MappingFastJsonValue[FastJsonContainer[Object]]
*/
public class FastJsonHttpMessageConverter extends AbstractHttpMessageConverter//
implements GenericHttpMessageConverter {
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 allFilters = new ArrayList(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;
}
}