You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

网站用户重复上传相同图片视频是否需重复存储至文件夹

问题背景
  • 正在开发支持用户创建附带图片/视频帖子的网站,当前存储架构为:数据库存储媒体文件的路径引用,实际图片、视频文件存放在站点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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 00:01:36