JS向Go服务上传文件时multipart表单文件名路径前缀丢失如何解决
前缀丢失的原因
Go标准库net/http解析multipart表单的filename字段时,内部调用了filepath.Base()方法对文件名做清洗,会自动去掉所有路径前缀,只保留最后一级的纯文件名。这个是标准库的默认安全设计,目的是兼容不同客户端上传时携带的操作系统绝对路径,同时避免恶意用户构造带路径遍历的文件名引发安全问题。
你在原始请求体里能看到完整前缀,说明前端传值是正常的,就是标准库解析阶段给做了截断。
保留前缀的两种方案
方案1:前端新增表单字段传路径(推荐)
不需要改现有上传逻辑,只需在FormData中额外新增对应字段存储完整路径,后端直接读取普通表单值即可,兼容性最高,也符合规范:
前端示例:
const formData = new FormData() const targetPath = `img/${hashKey}.png` const file = new File([fileBlob], targetPath, { type: 'image/png' }) formData.append('files', file) // 新增字段传完整路径 formData.append('file_paths', targetPath)
后端读取:
if err := r.ParseMultipartForm(32 << 20); err != nil { // 错误处理 return } // 路径列表和文件列表顺序一一对应 pathList := r.MultipartForm.Value["file_paths"] for idx, fileHeader := range r.MultipartForm.File["files"] { fullFilePath := pathList[idx] // 后续处理逻辑 }
方案2:后端手动解析multipart流
如果不想改前端代码,可以绕过标准库的自动解析逻辑,直接用MultipartReader读取原始part信息,拿到未清洗的原始文件名:
import ( "io" "mime/multipart" ) // 替换原来的ParseMultipartForm逻辑 reader, err := r.MultipartReader() if err != nil { // 错误处理 return } // 限制最大上传大小和之前的32M保持一致 maxSize := int64(32 << 20) r.Body = http.MaxBytesReader(w, r.Body, maxSize) for { part, err := reader.NextPart() if err == io.EOF { break } if err != nil { // 错误处理 return } defer part.Close() if part.FormName() == "files" { // 这里拿到的FileName就是带img/前缀的原始值 rawFileName := part.FileName() // 读取part的内容就是文件二进制流,自行处理存储即可 fileContent, err := io.ReadAll(part) if err != nil { // 错误处理 return } // 后续处理逻辑 } }
注意:使用带路径的文件名做存储时,一定要做好路径校验,限制最终生成的存储路径只能在你指定的业务目录范围内,避免出现路径遍历安全漏洞。
内容的提问来源于stack exchange,提问作者Guga Figueiredo
相关产品推荐
相关产品推荐

