应先用 filepath.base() 剥离路径再用 filepath.ext() 提取扩展名,因 file.filename 可能含路径、多点扩展名、空值或大小写不一;需结合 mime 类型校验防伪造。

直接用 filepath.Ext() 提取,但必须先确保拿到的是真实文件名(不是表单字段名),且要防 null 和多点扩展名。
为什么不能直接用 file.Filename 做字符串截取
因为 file.Filename 是前端传来的原始文件名,可能含路径(如 C:\fakepath\report.pdf)、多点(archive.tar.gz)、无扩展名(README)甚至空字符串。硬切 strrpos($filename, ".") 类逻辑在 Go 里不安全,也绕不开平台路径分隔符问题。
正确做法是先剥离路径,再提取扩展名:
-
filepath.Base(file.Filename)获取纯文件名(去掉目录部分) -
filepath.Ext()安全提取扩展名(返回.pdf,不是pdf) - 注意:
filepath.Ext("")返回空字符串,filepath.Ext("noext")也返回空字符串,需显式判断
c.FormFile() 拿到的 *multipart.FileHeader 里没有现成扩展名方法
Go 标准库和 Echo 都没封装“获取扩展名”这个动作——file.Header 只有 Filename、Size、Header 字段,不解析内容。所以你必须自己处理 file.Filename。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
示例代码片段:
file, err := c.FormFile("avatar")
if err != nil {
return c.JSON(http.StatusBadRequest, echo.Map{"error": "no file provided"})
}
basename := filepath.Base(file.Filename)
ext := filepath.Ext(basename)
if ext == "" {
return c.JSON(http.StatusBadRequest, echo.Map{"error": "missing extension"})
}
// ext 现在是 ".jpg" 或 ".zip" 这样的形式
验证扩展名时别只比对 filepath.Ext() 的结果
用户可能伪造 Content-Type 或改后缀上传恶意文件。仅靠扩展名白名单(如 []string{".png", ".jpg"})不够:
- 先用
ext做初步过滤(快、简单) - 再用
file.Open()读取前几百字节,调用http.DetectContentType()做 MIME 推断 - 两者都通过才算可信,比如
.php文件即使扩展名合法,MIME 是text/x-php就该拒收
多文件上传时每个 file 都要单独处理扩展名
用 c.MultipartForm() 拿到 form.File["files"] 后,不能假设所有文件扩展名一致。循环中每项都要走一遍 filepath.Base() + filepath.Ext() 流程:
for _, file := range files {
basename := filepath.Base(file.Filename)
ext := strings.ToLower(filepath.Ext(basename))
if ext != ".png" && ext != ".jpeg" {
return c.JSON(http.StatusBadRequest, echo.Map{"error": "only png/jpeg allowed"})
}
// …保存逻辑
}
容易忽略的是:Windows 上传可能带 .JPG 大写,filepath.Ext() 不转大小写,得自己 strings.ToLower()。










