Dubbox RestEasy 文件上传乱码解决
2017-03-21 18:46
453 查看
背景
最近开发Dubbox服务,使用了Http协议对PHP系统暴露了一些Service服务,但是在上传时出现了乱码,google没有发现好的解决方案,只能自己debug,发现是配置中缺少一项。解决方案
直接说解决方案:添加一个filter,filter内容如下:
“`
/**
* Servlet Filter设置编解码
*
* @author qiesai
*/
public class CharacterEncodingFilter implements Filter {
private static final String ENCODING_UTF_8 = "UTF-8"; @Override public void init(FilterConfig config) throws ServletException { } @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(ENCODING_UTF_8); response.setCharacterEncoding(ENCODING_UTF_8); request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, ENCODING_UTF_8); chain.doFilter(request, response); }
}
“`
这里的关键是InputPart.DEFAULT_CHARSET_PROPERTY这个attribute,设置为utf-8,会覆盖掉dubbox默认的us-ascii编码。
问题定位
这个问题的发生原因还是在于RestEasy框架解析上传数据的时候发生的,RestEasy使用:resteasy-multipart-provider包解析上传数据,也就是content-type为multipart/form-data的request,大致解析过程如下:1.框架确定Content-Type为multipart/form-data,这个常量定义在jax支持包的MediaType接口中;
2.框架将请求交给MultipartFormDataReader ,这个Reader注解为Provider,调用readForm方法,解析请求;
3.MultipartFormDataInputImpl的parse方法解析body内容,读取数据,乱码发生在这个过程中
MultipartFormDataInputImpl解析
MultipartFormDataInputImpl的parse方法是继承自父类:MultipartInputImpl的,解析过程稍微有点复杂,但是最终是通过构造PartImpl来表示每一个参数的。InputPart来表示Form表单中每一项参数,PartImpl是InputPart的一个实现类,构造的时候传入BodyPart,然后做解析,构造过程如下:
框架使用MultipartFormDataInputImpl读取body内容发生乱码,源代码注释如下。
// 使用注解,框架可以自动发现这个Provider @Provider @Consumes("multipart/form-data") public class MultipartFormDataReader implements MessageBodyReader<MultipartFormDataInput> { protected @Context Providers workers; public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return type.equals(MultipartFormDataInput.class); } /** * 这个方法是关键,RestEasy框架调用这个readFrom方法获取请求信息, **/ public MultipartFormDataInput readFrom(Class<MultipartFormDataInput> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { // 读取boundary,Content-Type中会包含这个boundary,类似如下: // Content-Type:multipart/form-data;boundary=---------------------------7d33a816d302b6 // 使用---------------------------7d33a816d302b6 作为参赛见的分割 String boundary = mediaType.getParameters().get("boundary"); if (boundary == null) throw new IOException(Messages.MESSAGES.unableToGetBoundary()); //实际上的解析器 MultipartFormDataInputImpl input = new MultipartFormDataInputImpl(mediaType, workers); input.parse(entityStream); return input; } }
PartImpl
这个类作用是用来表示body中每个form表单项,比如,你传递了name,sex,image,那么就会有三个PartImpl的实例,对应三个参数。构造过程如下:
public PartImpl(BodyPart bodyPart) { this.bodyPart = bodyPart; //选择ContentType,看Client是否传递了Content-Type,里面有可能包含编码信息; for (Field field : bodyPart.getHeader()) { headers.add(field.getName(), field.getBody()); if (field instanceof ContentTypeField) { contentType = MediaType.valueOf(field.getBody()); contentTypeFromMessage = true; } } //如果是null,则用默认的Content-Type,一般Client发送的Content-Type不为null,但是不包含编码; if (contentType == null) contentType = defaultPartContentType; //从ContentType中找编码字符,乱码时找不到,进入if内逻辑; if (getCharset(contentType) == null) { if (defaultPartCharset != null) //使用框架全局默认的编码,filter的作用就是设置这个。 { contentType = getMediaTypeWithDefaultCharset(contentType); } else if (contentType.getType().equalsIgnoreCase("text"))//没有默认,使用us-ascii编码,乱码发生。 { contentType = getMediaTypeWithCharset(contentType, "us-ascii"); } } }
MultipartInputImpl : 默认的Conten-Type定义及初始化位置
默认的Content-Type在MultipartInputImpl中,定义如下,这个是成员初始化的默认值,在构造的时候可以覆盖掉。// 实际上就是text/plain; charset=us-ascii protected MediaType defaultPartContentType = MultipartConstants.TEXT_PLAIN_WITH_CHARSET_US_ASCII_TYPE;
默认的defalutPartCharst的初始化时在构造MultipartInputImpl的时候,在构造方法中,这个对象,每次请求都会new一个,所以是和request相关的:
public MultipartInputImpl(MediaType contentType, Providers workers) { this.contentType = contentType; this.workers = workers; HttpRequest httpRequest = ResteasyProviderFactory .getContextData(HttpRequest.class); if (httpRequest != null) { // 从request的attribute中获取默认的编码和contentType String defaultContentType = (String) httpRequest .getAttribute(InputPart.DEFAULT_CONTENT_TYPE_PROPERTY); if (defaultContentType != null) this.defaultPartContentType = MediaType .valueOf(defaultContentType); this.defaultPartCharset = (String) httpRequest.getAttribute(InputPart.DEFAULT_CHARSET_PROPERTY); if (defaultPartCharset != null) { this.defaultPartContentType = getMediaTypeWithDefaultCharset(this.defaultPartContentType); } } }
相关文章推荐
- dojo小例子(17)iframe上传文件到rest后台,以及乱码问题解决
- Apache CXF REST 文件上传,中文乱码(已解决)
- JSP SmartUpload上传文件乱码解决纪实+UploadBean上传解决方案
- struts 上传文件乱码问题解决方法
- PHP中完美解决fckeditor上传中文文件与新建中文目录出现乱码的问题
- windos文件上传ubuntu出现乱码解决办法
- commons-fileupload.jar上传文件时的乱码解决
- java web 利用 commons-fileupload-1.2 实现文件上传 解决 中文乱码& 非文件属性的读取
- flex 利用commons-fileupload.jar 上传文件时 乱码解决方式
- commons-fileUpload组件解决文件上传乱码问题
- asp.net 将文件 二进制 上传 保存 再显示,解决文件流中文乱码问题。
- JSP SmartUpload上传文件乱码解决纪实+UploadBean上传解决方案
- FCKeditor 2.3中文文件上传时出现乱码的解决方法
- FTP 文件上传 解决中文乱码
- OpenSolaris 文件上传文件名乱码问题解决
- JSP SmartUpload上传文件乱码解决纪实+UploadBean上传解决方案
- struts2文件上传下载及乱码问题解决
- FCKeditor 2.3中文文件上传时出现乱码的解决方法
- 一、SmartUpload上传文件乱码解决纪实
- jspsmartupload上传文件,中文乱码解决方法