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 即可
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。