laravel防止同名文件并发上传冲突需三重防护:一、用redis锁(set nx+ex)按文件哈希+用户id加锁,失败返回423;二、数据库用sharedlock校验或insert ignore配合唯一索引防重复记录;三、临时路径+原子rename避免文件内容混杂。

当多个用户同时上传同名文件到Laravel应用时,可能出现覆盖、写入冲突或数据库记录错乱,必须在文件写入前确保同一文件标识(如哈希值或原始文件名+用户ID组合)的上传操作串行执行。
基于Redis锁实现单文件并发阻塞
第一步:安装predis扩展并配置Redis连接,确保config/database.php中redis.default已正确指向可用Redis实例。
第二步:在上传逻辑开始处生成唯一锁键,例如$lockKey = 'upload:file:' . md5($request->file('file')->getClientOriginalName() . $user->id),避免仅用文件名导致不同用户间误锁。
第三步:使用Redis SET命令带NX和EX选项尝试加锁:Redis::set($lockKey, 1, ['NX', 'EX' => 30]),失败则说明已有其他请求正在处理该文件,此时抛出HttpStatusCodeException(423)返回“Locked”响应,前端可提示“文件正被上传,请稍候重试”。
第四步:加锁成功后立即执行文件保存与数据库写入,完成后务必调用Redis::del($lockKey)释放锁——【不释放会导致后续所有同名上传永久阻塞】。
数据库行级锁防止重复记录插入
方法一:使用sharedLock()读取并锁定待检查记录
先通过DB::table('uploads')->where('hash', $hash)->sharedLock()->first()查询是否存在相同哈希的上传记录,若存在则跳过入库;此方式适用于已知记录大概率存在且需强一致性校验的场景。
方法二:直接INSERT IGNORE跳过冲突
执行DB::insert('INSERT IGNORE INTO uploads (hash, user_id, path, created_at) VALUES (?, ?, ?, ?)', [$hash, $user->id, $path, now()]),依赖数据库唯一索引(如UNIQUE KEY `unique_hash_user` (`hash`, `user_id`))拦截重复插入,无需手动加锁,性能更高但无法获取是否跳过的信息。
临时文件命名与原子写入规避覆盖风险
上传时不要直接用原始文件名保存,而是生成带时间戳和随机字符串的临时路径:$tempPath = storage_path('app/tmp/' . Str::random(12) . '_' . time() . '_' . $file->getClientOriginalName())。
将文件移动至临时路径后,再通过rename()系统调用原子地将临时文件重命名为目标路径——【Linux下rename是原子操作,能彻底避免多进程同时写入同一文件导致的内容混杂】。
这一步操作起来很简单,直接调用File::move($tempPath, $finalPath)即可完成。











