前端需自行检测文件覆盖:drop时比对已存文件名(统一转小写并trim),弹出非阻塞浮层供用户选择替换或跳过,上传成功后再移除旧节点并清理object URL,Safari及移动端须降级处理。

drop事件里怎么判断目标文件是否已存在
浏览器不会自动告诉你“这个文件名是不是已经上传过了”,所有覆盖检测都得前端自己做。核心思路是:在 drop 时,先查当前页面已有的附件列表(比如存在 data-filename 的 DOM 节点、或维护的 JS 数组),再比对新拖入的 file.name。
常见错误是只比对文件名,忽略大小写或空格差异;更稳妥的做法是用 file.name.trim().toLowerCase() 统一格式后再查。
- 如果用
URL.createObjectURL(file)预览过旧文件,记得同步清理旧的 object URL,否则内存泄漏 - 服务端通常也需校验(如返回 409 Conflict),但前端拦截能立刻反馈,避免无效上传
- 别用
file.lastModified做去重依据——用户复制文件后时间戳不变,但内容可能已改
覆盖前弹窗提醒为什么不能用 alert
alert() 会阻塞主线程、打断拖放流程,且移动端 Safari 会直接忽略。用户松手那一刻弹出 modal,体验断裂感极强。
真正可用的方案是:在 drop 回调里不立即上传,而是把文件暂存到一个临时变量,再渲染一个轻量级 <dialog></dialog> 或自定义浮层,让用户手动点「替换」或「跳过」。
把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。
立即学习“前端免费学习笔记(深入)”;
- 浮层必须绑定在 drop 区域内部,避免因滚动或 iframe 导致定位偏移
- 点击「替换」后才调用
formData.append('file', file)并发起 fetch - 点击「跳过」就清空临时变量,不触发任何网络请求
如何让覆盖操作可撤销
覆盖不是原子操作——上传新文件 + 删除旧记录,中间有网络延迟。一旦失败,用户可能丢失原文件。所以得保留旧文件的完整上下文。
推荐做法是在 DOM 中保留旧附件容器(比如 <div class="attachment">),仅隐藏它(<code>display: none),同时插入新预览节点。等上传成功响应后,再真正移除旧节点并调用 URL.revokeObjectURL()。
- 旧节点上要存
data-server-id和data-object-url,方便失败时还原 - 上传中状态用
aria-busy="true"标记,禁用重复操作 - 如果后端支持,可传
X-If-Match: <etag></etag>实现条件更新,避免并发覆盖
移动端和 Safari 的覆盖逻辑要单独处理
iOS 和 iPadOS 不支持文件拖放 API,所谓“拖拽覆盖”在这些设备上根本不会进入 drop 流程。Safari 桌面版对 dataTransfer.items 的解析也更保守,files 可能为空。
这意味着你写的覆盖检测逻辑,在 Safari 里可能压根没机会执行。必须降级到 <input type="file">,并在 change 事件里复用同一套文件名比对逻辑。
- 不要在 dragover/drop 里写覆盖提示,它们在 Safari 移动端完全不触发
- 用
if ('draggable' in document.createElement('span'))做基础能力检测,再决定启用哪套流程 - 即使桌面 Safari,也要 fallback 到 name + size 双校验,因为它的
file.type经常为空字符串


















