背景
由于同事前两天在做文件上传时,测试中文名文件上传后,后端获取到的文件名称为???.png,刚开始以为是前端编码问题,则排查后发现前后端均使用的是utf-8编码格式;在几次修改不同获取文件名方式后,还是存在文件名乱码问题,最终在本地用postman测试后发现,直接调用上传图片的模块,是不会出现这样的问题的;但是从对外接口模块上传图片则会出现文件名乱码问题,对外接口是通过网关进入的,猜测会不会是网关配置等问题导致,查资料后发现可能是zuul的问题导致。
文件名乱码
SpringCloud环境下Springboot 上传文件时, 中文文件名会出现乱码,文件名变成问号。但是,如果不通过网关zuul,上传文件就不会乱码,因此猜测是Zuul对上传的请求的编码进行了处理,最终在网上找到了两种解决方案。
原理
Spring Cloud Zuul现在对于上传文件有两种处理方式,一种是用spring mvc,另一种是zuulServlet。spring mvc对文件处理不是很好,会导致乱码问题,zuulServlet则不会。
在zuul的过滤器中,有一个pre的过滤器 ServletDetectionFilter,他的执行顺序是-3,也是最先执行的过滤器,在这个过滤器中,有这么一段代码:
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
if (!(request instanceof HttpServletRequestWrapper)
&& isDispatcherServletRequest(request)) {
ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, true);
} else {
ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, false);
}
return null;
}
在这个方法中,IS_DISPATCHER_SERVLET_REQUEST_KEY为false就会用ZuulServlet处理。如果没加/zuul前缀,IS_DISPATCHER_SERVLET_REQUEST_KEY就会置为true,就会用spring mvc上传。会出现乱码问题。
一般情况下,发送到API网关的外部请求都会被Spring的DispatcherServlet处理,除了通过/zuul/路径访问的请求会绕过DispatcherServlet,被ZuulServlet处理,主要用来应对处理大文件上传的情况。
处理方式
方案一:
在上传文件的请求路径之前添加字符串“zuul”声明此请求的编码不做处理。
例如在当前代码下,请求路径是:
localhost:9090/pay/uploadFile
那么采用方案一解决中文乱码的请求路径就是:
localhost:9090/zuul/pay/uploadFile
方案二:
在方案一的基础上,需要将前端代码的请求地址进行修改,如果地址固定死无法进行修改时,可以通过在zuul的配置文件中添加一个如下的属性:
zuul.servlet-path=/
注意是在网关的配置文件中添加。
总结
排查问题要找准问题发生的准确位置,刚开始没有定位到位置,以为是程序编码问题,排查后发现不是,后面还以为是springboot临时文件目录的编码问题,排查后发现还不是,最后根据postman访问对外接口与直接访问上传图片的模块后,才发现可能是Spring Cloud Zuul现在对于上传文件两种处理方式不同导致。所以解决问题前,一定要先准确定位问题。