表单上传文件名中文乱码讨论
关键字
form表单、POST、上传文件、文件名乱码、windows默认GBK
我有解决方案,但是希望有更好的能提供一下思路。
环境
windows10
golang:1.14.4
curl工具
问题描述
我在写一个http的接口,POST方式,表单上传文件。在windows系统使用curl工具上传后,文件名在后端接收到后出现了乱码。经过分析字节码,发现是GBK编码。而golang处理中文默认是以utf-8方式。并且在centos7系统下使用curl工具上传中文名字的文件不会出现这个问题。
问题分析
请求中的中文编码问题实在是太常见了。但好像没有一个完美的解决方案,尤其是在golang中。windows10中的系统文件夹名字是GBK编码,linux系统普遍是支持utf-8方式编码。我希望我的接口不论是在什么什么方式的请求都能正确的处理编码格式问题。
查了很多的相关资料,明确了几个事实,上传文件名字和内容的编码方式是无法通过上传时指定header改变的,也就是说http协议是不会帮忙转换这两者的编码方式。
后端可以转换编码方式,但是没法知道过来的数据本来是按照什么编码方式。
由此可见,没有一种自动识别编码或者是在客户端上传时就改变编码的方案。我的选择是,在上传的时候指定一个header:accept-charset:gbk
,以此来协商传输文件的编码方式以及服务端的解码方式。默认的我就当成utf-8。这个方案比较折中,能够处理不同的编码方式,但就是有些别扭,因为将一部分控制权交给了不可靠的客户端来决定。但目前没有其他更好的办法,而且即便是编解码错误了,错误的代价很低,就将就了。
方案
func init(originName string, rquest *http.Request){
if charset := request.Header.Get("accept-charset"); charset != "" {
charset := strings.ToLower(charset)
if charset == "gbk" {
utf8Bytes, _ := GbkToUtf8([]byte(originName))
originName = string(utf8Bytes)
}
}
return originName
}
func GbkToUtf8(s []byte) ([]byte, error) {
reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())
d, e := ioutil.ReadAll(reader)
if e != nil {
return nil, e
}
return d, nil
}
新版方案
在写完这篇博客几个月后,发现了golang的一个api,可以检查字节数组的编码方式,支持多种编码方式检查,其中包括了UTF-8。utf8.Valid()
。
就这样我,不用再传递自定义的header,。而是判断。
func init(originName string, rquest *http.Request){
isUtf8 := utf8.Valid(nameBytes)
if isUtf8 := utf8.Valid([]byte(originName)) {
charset := strings.ToLower(charset)
if charset == "gbk" {
utf8Bytes, _ := GbkToUtf8([]byte(originName))
originName = string(utf8Bytes)
}
}
return originName
}
总结
这样的方案只能是不完美的,对不同的编码方式处理的能力很弱。希望找到其他更好的方案。
再版后:找到了更好一点的方案,当然这也不是完美的,没办法检查是不是gbk编码格式,所以支持的比较弱,还是比较自动化了。