JS获取文件名并去除扩展名的方法
详细讲解在JavaScript和Node.js环境中获取文件名并去除扩展名的实用方法,涵盖字符串截取法、正则表达式法及原生path模块,深入解析多点号、隐藏文件及URL等边界场景的处理逻辑。
在前端表单上传展示、素材下载重命名,或者服务端的日志与文件处理场景中,我们常常拿到诸如 /uploads/2026/report-v1.draft.pdf 这样的完整路径或带后缀文件名。核心目标是从输入中稳妥地提取出文件的纯主名称(即 report-v1.draft),剥离冗余的父级目录和末尾的 .pdf。实现该需求的核心逻辑主要分为两步:先剔除路径中的目录层级,再依据末尾点号 . 的位置安全切除扩展名。本文将系统讲解字符串截取法、正则表达式法与 Node.js 原生路径解析法,并逐一分析边界情况。
一、基于字符串索引法:最通用、零依赖的基础方案
在所有执行环境中,使用原生字符串操作(lastIndexOf 配合 slice 或 substring)是性能最高、最直接的做法。不论是浏览器端通过 <input type="file"> 读取到的 file.name,还是服务端收到的文件相对路径,都可以先完成纯文件名提取,再执行扩展名剥离。
1. 完整实现代码
/**
* 从文件路径或名称中提取无扩展名的文件名
* @param {string} fullPath - 完整路径或文件名(如 "/path/to/archive.v1.tar.gz")
* @returns {string} 纯文件名主体
*/
function getFileNameWithoutExt(fullPath) {
if (typeof fullPath !== 'string' || !fullPath.trim()) {
return '';
}
// 1. 提取带后缀的纯文件名,兼容 Windows (\) 与 POSIX (/) 路径分隔符
const normalizedPath = fullPath.trim();
const lastSlashIndex = Math.max(
normalizedPath.lastIndexOf('/'),
normalizedPath.lastIndexOf('\\')
);
const baseName = lastSlashIndex >= 0 ? normalizedPath.slice(lastSlashIndex + 1) : normalizedPath;
// 2. 查找最后一个点号的位置
const lastDotIndex = baseName.lastIndexOf('.');
// 3. 边界校验:若无点号,或点号在首位(如 .gitignore 隐藏文件),则返回原名称
if (lastDotIndex <= 0) {
return baseName;
}
// 4. 切片截取:保留从索引 0 到最后一个点号之前的内容
return baseName.slice(0, lastDotIndex);
}
// 验证示例
console.log(getFileNameWithoutExt("photo.png")); // 输出: "photo"
console.log(getFileNameWithoutExt("D:\\documents\\budget.xlsx")); // 输出: "budget"
console.log(getFileNameWithoutExt("bundle.min.js")); // 输出: "bundle.min"
console.log(getFileNameWithoutExt(".gitignore")); // 输出: ".gitignore"(隐藏文件安全保留)
console.log(getFileNameWithoutExt("LICENSE")); // 输出: "LICENSE"(无后缀文件正常输出)
二、必须处理的特殊场景与边界陷阱
很多初学者在写去除扩展名时,会简单地使用 fileName.split('.')[0]。这种写法在应对复杂的现实场景时极易引发 Bug。在实际业务中,必须防范以下几类特殊文件名:
- 多点号与版本号(如
app.min.js或dataset.2026.03.csv):如果使用split('.')[0],会导致文件名被过早切断为app或dataset,遗失了核心的版本标识。使用lastIndexOf('.')则只会剥离末尾的.js或.csv,保留完整的app.min与dataset.2026.03。 - 隐藏文件(Dotfiles,如
.gitignore、.env.production):在 UNIX 风格系统中,以点开头的通常是隐藏配置。.gitignore的点号索引为 0。如果直接slice(0, 0),将得到空字符串""。因此当lastDotIndex <= 0时,必须判定为无扩展名或者隐藏文件本体,全名返回。 - 无扩展名文件(如
Dockerfile、LICENSE):查找点号会返回-1。如果不做判定直接切片(如slice(0, -1)),会导致文件名的最后一个字符被错误吞掉(变成Dockerfil)。 - 网络 URL 携带查询参数或锚点:如
https://cdn.example.com/assets/sheet.xlsx?token=xyz#title。在截取文件名之前,需先剔除?和#。
三、基于正则表达式法:简洁优雅的一行式处理
如果不需要复杂的中间状态排查,只希望用简洁的一行代码剥离文件名后缀,正则表达式是非常高效的工具。我们可以利用正则的“末尾锚定”特性,只精准匹配最后一个点号及其后的非点字符。
1. 仅针对纯文件名去除扩展名
// 匹配规则:以点开头,后面跟随不包含斜杠和点的一个或多个字符,并以行尾结束
const stripExtRegex = /\.[^/.]+$/;
function removeExtension(filename) {
if (!filename || typeof filename !== 'string') return '';
// 必须排除以点开头的隐藏文件(如 .gitignore)
if (filename.startsWith('.') && filename.indexOf('.', 1) === -1) {
return filename;
}
return filename.replace(stripExtRegex, '');
}
console.log(removeExtension("archive.tar.gz")); // "archive.tar"
console.log(removeExtension("styles.module.css")); // "styles.module"
console.log(removeExtension(".env")); // ".env"
2. 从完整路径直接提取纯文件名(一步到位)
若希望从整条路径中一次性提取纯文件名,可以组合“路径贪婪匹配”与“后缀剥离”正则:
function extractBaseNameRegex(pathStr) {
if (!pathStr || typeof pathStr !== 'string') return '';
// 匹配最后一个斜杠之后、最后一个点之前的内容(若无点则取至末尾)
const match = pathStr.match(/(?:.*[/\\])?([^/\\]+?)(?:\.[^/.]+)?$/);
return match ? match[1] : '';
}
console.log(extractBaseNameRegex("/var/data/users/list.json")); // "list"
console.log(extractBaseNameRegex("C:\\workspace\\image.png")); // "image"
四、Node.js 原生路径模块方案(服务端首选)
如果代码运行在 Node.js(或者支持兼容规范的 Bun / Deno)环境中,最稳妥也是官方标准做法是使用内置的 node:path 模块。原生模块针对不同操作系统的路径分隔符(Windows 上的 \ 和 POSIX 系统上的 /)做好了全自动兼容,能够完全免去开发者自行切分斜杠的负担。
1. 使用 path.parse() 直接获取
path.parse() 会将完整路径解析成一个结构化对象,其中 name 属性正是去除扩展名后的纯文件名:
import path from 'node:path';
const filePath = '/var/logs/2026/server.audit.log';
const parsed = path.parse(filePath);
console.log(parsed);
/* 输出示例:
{
root: '/',
dir: '/var/logs/2026',
base: 'server.audit.log',
ext: '.log',
name: 'server.audit'
}
*/
console.log(parsed.name); // 'server.audit'
2. 使用 path.basename() 二次剔除
如果只需要纯文件名,也可以使用 path.basename(),该方法接受第二个参数指定要从结果末尾移除的后缀字符串:
import path from 'node:path';
const filePath = 'C:\\workspace\\project\\app.bundle.js';
// path.extname 会准确提取末尾扩展名 '.js'
const ext = path.extname(filePath);
const fileName = path.basename(filePath, ext);
console.log(fileName); // 'app.bundle'
path.posix.parse();反之,若在 Linux 上处理 Windows 路径,则使用 path.win32.parse()。
五、全流程技术选型与综合决策
为了让不同环境下的代码兼具健壮性与简洁性,在选择实现方案时可以参考以下决策路径:
常见方案选型速查表
| 方案类型 | 核心 API / 语法 | 运行环境 | 多点号兼容性 | 隐藏文件表现 | 综合推荐度 |
|---|---|---|---|---|---|
| 字符串索引封装 | lastIndexOf + slice |
浏览器 / Node 通用 | 优秀(仅切最后后缀) | 需判断 idx <= 0 |
★★★★★(前端首选) |
| 单行正则表达式 | replace(/\.[^/.]+$/, '') |
浏览器 / Node 通用 | 优秀 | 需额外排除首位点 | ★★★★☆(轻量推荐) |
| Node 原生模块 | path.parse(p).name |
Node.js / Bun / Deno | 极佳 | 原生完善适配 | ★★★★★(后端首选) |
| 朴素 split 分割 | str.split('.')[0] |
不推荐 | 极差(版本号被破坏) | 产生空字符串 | ★☆☆☆☆(高危弃用) |
综上所述,获取文件名并去除扩展名的实质是对特定字符下标的精确定位与防御性切片。在日常编码中,避免使用朴素的 split('.')[0],根据环境选用封装严密的 lastIndexOf 函数或 Node.js 原生 path.parse(),即可编写出在各种复杂边界下均稳健可靠的代码。
Node.js中使用path模块获取文件名
2026-11-05 15:15:15
Node.js中url模块与querystring模块处理查询字符串的区别
2026-11-05 15:04:45
JavaScript(JS)主要用来做什么
2026-11-05 10:10:47
怎么修改Node.js和npm的全局安装路径与缓存目录
2026-11-04 19:48:28
NW.js 常见问题排查:Node 模块加载失败与版本兼容解决
2026-11-04 11:22:45
相关标签:








