网站用户重复上传相同图片视频是否需重复存储至文件夹
问题背景
- 正在开发支持用户创建附带图片/视频帖子的网站,当前存储架构为:数据库存储媒体文件的路径引用,实际图片、视频文件存放在站点
pictures/目录下,后续规划将全量媒体资源迁移至云存储。 - 现有代码存在逻辑风险:用户重复上传同名文件时,新文件会直接覆盖目录内已有同名文件,参考Reddit、X(原Twitter)等同类平台的实现,不确定重复上传场景下应该为每次上传生成独立存储副本,还是允许覆盖共用同一份文件。
当前上传功能代码
<?php session_start(); include_once 'includes/dbh.inc.php'; $id = $_SESSION["userid"]; if(isset($_POST["submitimgvid"])){ $title = $_POST["title"]; $users_id = $_POST["users_id"]; $content = $_POST["content"]; $date_created = $_POST["date_created"]; $file = $_FILES['file']; $fileName = $_FILES['file'] ['name']; $fileTmpName = $_FILES['file'] ['tmp_name']; $fileSize = $_FILES['file'] ['size']; $fileError = $_FILES['file'] ['error']; $fileType = $_FILES['file'] ['type']; $fileExt = explode('.', $fileName); $fileActualExt = strtolower(end($fileExt)); $allowed = array('jpg', 'jpeg', 'png', 'mp4'); $fileNameNew = $fileName.$id.".".$fileActualExt; $fileDestination = 'pictures/'.$fileNameNew; if(in_array($fileActualExt, $allowed)){ if($fileError === 0){ if($fileSize < 50000000){ move_uploaded_file($fileTmpName, $fileDestination); $sql = "INSERT INTO media (title, users_id, content, date_created, imagepath) VALUES ('$title','$id','$content','$date_created', '$fileDestination');"; $result = mysqli_query($conn, $sql); header("Location: home.php?uploadsuccess"); } else { echo "Your file is too big!"; } } else { echo "There was an error uploading your file!"; } } else { echo "You cannot upload files of this type!"; } }
核心疑问
用户重复上传相同图片/视频时,应该重新上传生成独立存储文件,还是覆盖原有文件共用存储资源?
解决方案
绝对不要实现文件覆盖逻辑,这是会导致内容错乱的严重bug。你当前的命名规则是原文件名+用户ID.后缀,只要用户上传同名同后缀文件就会触发覆盖,直接导致之前引用该路径的老帖子配图/视频被替换成新内容。比如用户第一次传cat.jpg发晒猫帖,第二次传同名cat.jpg发工作吐槽帖,后续再访问第一条晒猫帖时,就会错误显示吐槽帖的配图,内容完全错乱。
你可以根据业务所处阶段,从以下两种成熟方案里选:
方案1:每次上传生成全局唯一独立文件(当前阶段首选,改造成本极低)
- 放弃用原文件名拼接用户ID的命名规则,给每一次上传的文件生成不会重复的独立文件名,从根源上杜绝重名覆盖问题。文件名可以拼接用户ID、毫秒级时间戳、随机字符串生成,参考实现:
// 生成几乎不可能重复的文件名:用户ID+时间戳+6位随机字符串+文件后缀 $randomStr = bin2hex(random_bytes(3)); $fileNameNew = $id.'_'.time().'_'.$randomStr.'.'.$fileActualExt; $fileDestination = 'pictures/'.$fileNameNew;
- 这个方案逻辑极简,不需要做文件内容比对,后续迁移云存储时也不需要调整逻辑——主流云存储本身就推荐用唯一对象键存储文件,天然避免重名冲突。哪怕用户连续100次上传完全相同的文件,每次生成的都是独立资源,互不影响,后续删除某条帖子对应的媒体文件时,也不会波及其他帖子的正常展示。
- 该方案唯一的缺点是会产生重复文件占用额外存储,但对于业务早期来说,存储成本远低于内容错乱bug带来的用户体验损失,性价比极高。
方案2:基于文件内容哈希做存储去重(业务规模上涨后可选的优化方案)
- 等后续业务量达到一定级别,觉得重复文件占用的存储成本过高时,再做内容去重即可:上传文件时先读取文件内容计算SHA256/MD5哈希值,先查询媒体库中是否已存在哈希值完全一致的文件,如果存在直接复用已有文件的路径,不需要重复写入存储;如果不存在,就按方案1的唯一命名规则存储新文件,同时把文件哈希值存在媒体表的对应字段中。
- 注意去重判断必须基于文件内容哈希,绝对不能用文件名判断是否重复——同名文件的内容可能完全不同。另外做文件复用时需要给媒体资源加引用计数,删除帖子时不要直接删除物理文件,先把对应资源的引用计数减1,等计数归0时再执行实际的文件删除操作,避免误删被其他帖子引用的资源。
顺带提两个你现有代码的高危问题,建议同步修复:
- 现有SQL语句直接拼接用户传入的参数,存在严重SQL注入风险,务必改成PDO或mysqli预处理语句传参,不要直接把变量拼进SQL字符串。
- 不要信任前端传过来的
users_id、date_created参数,用户ID直接取session中存储的登录态值即可,帖子创建时间用数据库的CURRENT_TIMESTAMP函数生成,避免用户恶意篡改参数。
内容的提问来源于stack exchange,提问作者Hamodi coolguy
相关产品推荐
相关产品推荐

