您现在的位置: 365建站网 > 365文章 > HTML 文件上传路径变成 C:\fakepath 怎么解决?原因与正确读文件名

HTML 文件上传路径变成 C:\fakepath 怎么解决?原因与正确读文件名

文章来源:365jz.com 点击数:12 更新时间:2024-06-15 19:09

写上传功能时几乎都会撞上这一幕:用户明明选了 D:\docs\报表.pdf,JS 里读 input.value 拿到的却是 C:\fakepath\报表.pdf。有人以为是 bug 想去“修复”,其实这是浏览器故意伪造的路径,无法通过正常手段关闭。本文解释原因,并给出正确获取文件信息的方式。

input file 上传后 C fakepath 的原因与正确读文件名

一、MDN 的官方定义

MDN 在 <input type="file"> 页面(https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input/file )写得很直接:

“为了阻止恶意软件猜测文件路径,该值的字符串表示总是以 C:\fakepath\ 为前缀的文件名,而并不是文件的真实路径。”

也就是说:input.value 的字符串形态里,路径部分被固定替换成 C:\fakepath\,只有文件名是真实的。同时它也提到另一个细节——用户选了多个文件时,value 只表示第一个文件,其余要用 HTMLInputElement.files 属性去读。

二、为什么要这么做

早期(IE 时代、以及某些老浏览器配置下)确实能拿到完整路径。但这条信息属于用户本地隐私:路径往往暴露用户名、组织架构、软件安装习惯(如 C:\Users\zhangsan\Desktop\公司\合同终版\),配合其它指纹信息还可能被用于用户识别与定向攻击。现代浏览器统一用 fakepath 抹掉目录部分。

历史上几个逃生门都已被废弃:

  • IE 的 “提交非上传文件的完整路径” 安全选项(Submit non-file uploads with full path)——随 IE 一起消失;
  • Firefox 的 security.fileuri.strict_origin_policy 一类 about:config 开关——不影响该行为;
  • Flash 时代的上传控件能读全路径——插件已停服。

结论:网页里拿不到用户文件的绝对路径,这是设计而非缺陷。需要真实磁盘路径的场景(批量本地处理)应该用原生客户端程序,或 File System Access API。

三、正确读取文件名与属性

标准做法是走 files(FileList),它的每一项是 File 对象:

const el = document.querySelector('#avatar');
el.addEventListener('change', () => {
  for (const f of el.files) {
      console.log(f.name, f.size, f.type, f.lastModified);
  }
  // 只要第一个文件名
  const name = el.files.length ? el.files[0].name : '';
});

如果历史代码里已经在用 value 且不想改成 FileList,可以截掉伪路径前缀(Windows 与 Mac 分隔符都要考虑):

function realName(v) {
  const i = Math.max(v.lastIndexOf('/'), v.lastIndexOf('\\'));
  return i < 0 ? v : v.slice(i + 1);
}
realName('C:\\fakepath\\月度报表.xlsx');  // 月度报表.xlsx

四、后端拿到的也是文件名

表单上传时,浏览器发送的 Content-Disposition 里也只带文件名,不带路径:

Content-Disposition: form-data; name="avatar"; filename="月度报表.xlsx"

PHP 接收端 $_FILES['avatar']['name'] 得到的同样是 月度报表.xlsx。有些老代码会在这里做 basename() 二次清洗——这是对的,因为文件名本身也不能信任:filename="../../wwwlogs.php" 这类路径穿越、以及带 ; 的空文件名都出现过。

服务端安全清单:

  • 用 basename() + 随机化重命名(date('Ymd').bin2hex(random_bytes(8)))存文件,绝不用原名直接落盘;
  • 校验扩展名白名单,同时校验 MIME(注意客户端上报的 type 可伪造,PHP 侧用 finfo_file() 读魔数);
  • 上传目录禁执行:Nginx location /uploads { location ~ \.php$ { deny all; } },Apache .htaccess 里 php_flag engine off;
  • 限制 upload_max_filesize 与 post_max_size,并让 Nginx 的 client_max_body_size 不小于 PHP 设置,否则会得到 413。

五、其它相关限制

  • accept 属性只是筛选提示,不是校验。MDN 明确 accept 支持扩展名与 MIME 两类写法(如 accept=".doc,.docx,application/msword")。
  • 想读文件内容做预览:用 FileReader 或 URL.createObjectURL(f),不需要路径也能工作。
  • 真需要访问磁盘目录结构:File System Access API(window.showDirectoryPicker())在 Chrome/Edge 可用,且必须由用户手势触发、每次都要授权。
  • webkitdirectory(选整个目录)是非标准属性,MDN 建议“除非别无选择否则不要用”,但 Firefox 50+ 与 Edge 均已支持。

六、常见问题 FAQ

问:怎么让 Chrome 显示真实路径?
答:没有开关。任何声称能“解除 fakepath”的插件或注册表方案,都是过时的或伪造的。

问:Electron 里能拿到吗?
答:可以。Electron 是原生进程,配合 dialog.showOpenDialog() 拿到的路径是真实的;但渲染进程里通过 <input type=file> 选的仍是 fakepath,需要用 webUtils.getPathForFile()(新版替代已废弃的 File.path)。

问:上传后想保留原文件名展示,怎么做到既安全又不丢名字?
答:落盘用随机名,另外把原始文件名存进数据库字段用于展示,下载时用 Content-Disposition: attachment; filename*=UTF-8''...(RFC 5987 编码)还原中文名。

一句话:C:\fakepath\ 是浏览器给用户路径上的“马赛克”。想读名字就取 files[0].name,想读内容就用 FileReader,别再去搜索“如何关闭 fakepath”。

生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »