最近开发中遇到了一个文件上传乱码问题,经过一顿百度谷歌以后,解决了问题以后,现在把这个问题的前世今生都记录下。
一、问题描述
从前端上传文件后,后端接受到文件,显示文件名
//此时name=????全是问号,还非乱码
String name = multipartFile.getOriginalFilename();
二、问题原因分析
为什么会出现乱码?因为请求时,网关会先对请求进行解码,以便它们可用Zuul过滤器的可能修改,然后在过滤器构建过程后重新对请求进行编码,重新编码的过程由于编码不一致导致乱码。
在ServletDetectionFilter过滤中
Spring Cloud Zuul现在对于上传文件有两种处理方式,一种是用spring mvc,另一种是zuulServlet。spring mvc对文件处理不是很好,会导致乱码问题,zuulServlet则不会。
在zuul的过滤器中,有一个pre的过滤器 ServletDetectionFilter,他的执行顺序是-3,也是最先执行的过滤器,在这个过滤器中,有这么一段代码:
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.zuul.filters.pre;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
import org.springframework.web.servlet.DispatcherServlet;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import com.netflix.zuul.http.ZuulServlet;
/**
* Detects whether a request is ran through the {@link DispatcherServlet} or {@link ZuulServlet}.
* The purpose was to detect this up-front at the very beginning of Zuul filter processing
* and rely on this information in all filters.
* RequestContext is used such that the information is accessible to classes
* which do not have a request reference.
* @author Adrian Ivan
*/
public class ServletDetectionFilter extends ZuulFilter {
public ServletDetectionFilter() {
}
@Override
public String filterType() {
return "pre";
}
/**
* Must run before other filters that rely on the difference between
* DispatcherServlet and ZuulServlet.
*/
@Override
public int filterOrder() {
return -3;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
if (!(request instanceof HttpServletRequestWrapper)
&& isDispatcherServletRequest(request)) {
ctx.set(RequestUtils.IS_DISPATCHERSERVLETREQUEST, true);
} else {
ctx.set(RequestUtils.IS_DISPATCHERSERVLETREQUEST, false);
}
return null;
}
private boolean isDispatcherServletRequest(HttpServletRequest request) {
return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
}
}
以下是常量定义代码
public class RequestUtils {
public static final String IS_DISPATCHERSERVLETREQUEST = "isDispatcherServletRequest";
public static boolean isDispatcherServletRequest() {
return RequestContext.getCurrentContext().getBoolean(IS_DISPATCHERSERVLETREQUEST);
}
public static boolean isZuulServletRequest() {
//extra check for dispatcher since ZuulServlet can run from ZuulController
return !isDispatcherServletRequest() && RequestContext.getCurrentContext().getZuulEngineRan();
}
}
在这个方法中,IS_DISPATCHER_SERVLET_REQUEST_KEY为false就会用ZuulServlet处理。如果没加/zuul前缀,IS_DISPATCHER_SERVLET_REQUEST_KEY就会置为true,就会用spring mvc上传。
三、解决办法
第一种解决方案,api接口前面 /zuul:
在不修改zuul默认配置参数的情况下,在上传文件的请求路径之前添加字符串“zuul”声明此请求被ZuulServlet处理。
例如在当前代码下,请求路径是:
localhost:8080/up/upload
那么采用方案一解决中文乱码的请求路径就是:
localhost:8080/zuul/up/upload
经过测试,中文乱码问题成功解决。
如果第一种方式,显示404,尝试第二种解决方案,改zuul的配置文件:
在zuul的配置文件中添加一个如下的属性:
zuul.servlet-path=/