banner
ZetoHkr

ZetoHkr

摸🐟从未停止,努力从未开始
github

解决Spring Cloud通过Zuul Gateway上传文件中文名乱码

前置环境#

JDK:8u423

Spring Cloud:Edgware.SR6(可谓是很古老的版本了)

Gateway:Zuul(spring-cloud-starter-netflix-zuul - 1.4.7.RELEASE)

起因#

之前在公司开发的时候,遇到了一个奇怪的问题,上传文件,中文的文件名(指通过 MulipartFile 的 getOriginalFileName () 的方法获取的文件名)会变成 “???” 乱码,起初,我以为是 Java 环境和操作系统环境的编码不正确,经过一番检查,发现都是 UTF-8 的编码,跟怪啊,很怪。

排查#

既然环境和系统都没问题,那就是代码的问题了,经过进一步的追查,发现在单体项目中文件名并没有乱码,只有在经过 Zuul 网关的时候才会出现乱码,在网上也尝试搜索了一下,基本上就给出 2 种解决方案:

  1. 在网关中加配置 application.yml
zuul:
    servlet-path: /
  1. 在请求前面加 zuul
    例如,实际的 api 是 /upload-file 乱码
    改成 /zuul/upload-file 解决

但是上面并不能解决大部分情况,比如我使用了方法 1,POST 请求直接无响应,方法 2 会 404,又经过了一段时间的排查,我们将目光对准 “FormHttpMessageConverter”(表单 HTTP 消息转换器)这个类,这个类很可疑。已知上传文件使用的是 Form Data,又可以根据相关文档可以知道,当我通过网关上传文件的时候会经过这个类,当我们阅读这个类的源代码的时候,我发现了一个可疑的地方:

FormHttpMessageConverter 类的作用是可以将 "application/x-www-form-urlencoded" 媒体类型读取并写入为 MultiValueMap<String, String>,也可以将 "multipart/form-data" 和 "multipart/mixed" 媒体类型写入为 MultiValueMap<String, Object>(但不能读取)。
原文 Java doc

这里为什么是 ASCII 编码呢?先粗略的分析一下,ASCII 一共就那百来个字符,哪里会转换中文呢?根据这个原因,我们再继续分析这个源代码,我们知道这个内联类 “MultipartHttpOutputMessage” 会转换请求标头,然后我们又知道 MultipartFile 会把文件名(Original File Name)放在 HTTP 请求标头里面(参考链接);这样我们就说的通了,既然知道了大概原因,我们就可以尝试修复这个问题了。

修复#

这里就简单粗暴的修一下(指修改 Spring Framework 源代码)(逃,其实想顺便编译一下 Spring Framework 的源代码

其实也可以升级依赖(如果不知道升级会带来什么后果的话,不建议直接升级依赖

其实修复这个问题很简单,只需要这样然后那样最后在这样,只需要改这个类的这里

Spring Framework 4.3.x 源代码

将红框处改为 return name.getBytes("UTF-8"); 即可。

Tips:其实直接看现在的主要分支的代码就可以知道,现在已经默认 UTF-8 编码了。(如图)
Spring Framework 主要分支源代码

其他说明#

编译 Spring Framework#

  • JDK 用 1.8
  • 编译的时候一定要先读 README.md
  • 把 build.gradle 的仓库地址改成如下的样子(因为有些用不了,或者被墙了),要改 2 处,一个是 buildscript 下面的,还有一个 configure (allprojects) 下面的
repositories {
        // mavenCentral()
        // gradlePluginPortal()
        // jcenter()
        maven { url 'https://repo.spring.io/snapshot' }
        // maven { url "https://repo.spring.io/plugins-release" }
        maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
        //阿里云新库
        maven { url "https://maven.aliyun.com/repository/central" }
        maven { url "https://maven.aliyun.com/repository/google" }
        maven { url "https://maven.aliyun.com/repository/gradle-plugin" }
        maven { url "https://maven.aliyun.com/repository/jcenter" }
        maven { url "https://maven.aliyun.com/repository/spring" }
        maven { url "https://maven.aliyun.com/repository/spring-plugin" }
        maven { url "https://maven.aliyun.com/repository/public" }
        maven { url "https://maven.aliyun.com/repository/releases" }
        maven { url "https://maven.aliyun.com/repository/snapshots" }
        maven { url "https://maven.aliyun.com/repository/grails-core" }
        maven { url "https://maven.aliyun.com/repository/mapr-public" }
        maven { url "http://repo.springsource.org/plugins-release" }
    }
  • 正常情况下执行的命令:
    ./gradlew cleanIdea :spring-oxm:compileTestJava
    ./gradlew install
    ./gradlew build -x test
    
  • 如果想上传到私有仓库,可以在项目根目录下的 gradle/publish-maven.gradle 文件中编写如下代码(把代码中的中文换成你需要的内容):
    uploadArchives {
        onlyIf { project.file('build/libs').exists() }
        repositories {
            mavenDeployer {
                customizePom(pom, project)
                repository(url: "这里写你的发布仓库地址") {
                    authentication(userName: 仓库用户名, password: 仓库密码)
                }
                snapshotRepository(url: "这里写你的快照仓库地址") {
                    authentication(userName: 仓库用户名, password: 仓库密码)
                }
                pom.version = version
            }
        }
    }
    
  • 然后再执行 ./gradlew uploadArchives 即可
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。