【问题标题】:BLOB data fails to be written to a databaseBLOB 数据写入数据库失败
【发布时间】:2021-12-24 02:26:33
【问题描述】:

我创建了一个数据库,用于存储有关图像以及图像本身的各种数据。我有五个键(id, name, size, format, file)id 是自动递增的。 file 是一个 BLOB 类型的值 - (mediumblob)。休息是其各自的类型。奇怪的是,除了 BLOB 图像本身之外,所有其他变量都成功写入数据库。

等待用户使用$_POST上传图片:

if(isset($_POST['file_one_submit'])){
    $post = 'file_one_input';
}

定义所有变量。包括 $file 变量:

$file = file_get_contents($_FILES[$post]['tmp_name']);

然后我进行查询,用新信息替换数据库中的记录:

$stmt = $conn -> prepare("REPLACE INTO `images` (id, name, size, format, file) VALUES(?, ?, ?, ?, ?);");
$stmt -> bind_param("isisb", $id, $name, $size, $format, $file);
$stmt -> execute();

执行查询后,数据库中的所有记录都成功更新,但应包含 BLOB 数据的 file 列除外。我认为$post 变量可能定义不正确,但例如获取上传文件的名称是可行的:

upload_name = $_FILES[$post]['name'];

我尝试回显 $file 变量以检查其内容,并得到一长串数据,我认为这些数据是在使用 file_get_contents() 函数转换为 BLOB 期间生成的正确字符串。

我检查了文件的大小没有超出mediumblob BLOB 类型的能力。我还尝试使用this 帖子检查我的代码中的错误,但没有成功。运行代码时,页面上不会出现错误或警告。我还使用E_STRICT 启用了错误/警告检查,但仍然没有显示输出。

【问题讨论】:

  • 您最终得到的记录中file 的值是多少,您想要存储的值是多少?另外,请在您的数据库中运行 show create table images 并向我们展示该表的外观。
  • 您在问题中遗漏了非常重要的信息。服务器上max_allowed_packet 的大小是多少,$file 的字节大小是多少。请将此信息添加到问题中。
  • 您将使用该文件做什么?如果它将在网页中使用,请参阅我的答案。如果它会以其他方式使用,我可能会有不同的建议。

标签: php mysql mysqli blob


【解决方案1】:

我使用send_long_data 方法让它工作了。 (在这些示例中,我删除了一些与示例无关的列)。

方案一:使用 send_long_data 方法传递二进制数据

根据https://www.php.net/manual/en/mysqli-stmt.bind-param.php使用b的好处:

注意:

如果变量的数据大小超过最大值。允许的数据包大小 (max_allowed_pa​​cket),您必须在类型中指定 b 并使用 mysqli_stmt_send_long_data() 以数据包的形式发送数据。

//...

if(isset($_POST['file_one_submit'])){
    $post = 'file_one_input';

    $id = 1;
    $file = NULL;

    $stmt = $conn -> prepare("REPLACE INTO `images` (id, file) VALUES(?, ?);");
    $stmt->bind_param("ib", $id, $file);
    
    //send_long_data(int $param_num, string $data): bool
    $stmt->send_long_data(1, file_get_contents($_FILES[$post]['tmp_name'])); 
    $stmt->execute();
}
?>

<form action="file.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file_one_input" />
    <input type="submit" name="file_one_submit" value="Upload" />
</form>

选项 2:将 b 更改为 s

我认为最好使用 b,但是如果我将 b 更改为 s(字符串),这似乎也可以工作:

//...

if(isset($_POST['file_one_submit'])){
    $post = 'file_one_input';

    $id = 1;
    $file = file_get_contents($_FILES[$post]['tmp_name']);

    $stmt = $conn -> prepare("REPLACE INTO `images` (id, file) VALUES(?, ?);");
    $stmt->bind_param("is", $id, $file);
    $stmt->execute();
}
?>

<form action="file.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file_one_input" />
    <input type="submit" name="file_one_submit" value="Upload" />
</form>

【讨论】:

  • 将“b”作为“blob”更改为“s”作为“string”后,一切正常,BLOB 数据成功写入。我知道 BLOB 基本上只是一堆“字符串”数据,但是如果传递“s”而不是“b”确实有效但不是其他方式,那么它让我混淆了实际使用的“b”数据类型是什么?这意味着“b”选项将完全没用......我一小时后自己认为这是一个解决方案,但既然你提到了同一个解决方案,我也会将其标记为答案,考虑到这可能并不完全那么做错了吗?感谢您指出了这一点。会有
  • 这是正确答案。感谢您阅读文档并提供它。我将提供解释作为另一个答案。
  • 使用 b 数据将以数据包的形式发送,以避免超出最大允许数据包大小。所以这就是b的好处。
【解决方案2】:

选项b用于批量发送数据。当您的数据大小超过max_allowed_packet(默认为16MB)时使用它。然后您必须使用方法send_long_data() 以数据包的形式发送数据。

// $file should be set to NULL
$stmt->bind_param("ssssb", $id, $name, $size, $format, $file);
$fp = fopen($_FILES[$post]['tmp_name'], "r");
while (!feof($fp)) {
    $stmt->send_long_data(4, fread($fp, 8192));
}
fclose($fp);
$stmt->execute();

当您确定您的 blob 数据永远不会超过限制时,您可以安全地将其绑定为字符串。默认情况下,您应该将每个变量绑定为字符串 (s),因为在大多数情况下类型并不重要。在 99% 的情况下,将所有内容绑定为字符串更容易、更安全。

通常,我推荐 PDO 作为更简单的解决方案,但即使使用PDO with large objects,您仍然必须以类似的方式处理流。

【讨论】:

    【解决方案3】:

    如果图像用于网页,我建议不要将图像存储在数据库中。相反,写入其他网页/文件树中的文件。然后构建&lt;img src=$path&gt;,其中$path 是相对于Web 文件的根目录。

    这避免了在这个问题中被问到的讨厌的问题,使数据库更有效率,并且实际上加快了网页的交付速度。

    把路径放在表格里,这样构建网页的时候就可以构建img标签了。

    【讨论】:

      【解决方案4】:

      因此您可以使用 PHP Streams API 对其进行操作。

      $conn = new PDO('database settings');
      $stmt = $conn->prepare("REPLACE INTO `images` (id, name, size, format, file) VALUES(?, ?, ?, ?, ?);");
      
      
      $file_tmp = $_FILES['file']['tmp_name'];
      $fp = fopen($file_tmp, 'rb');
      
      $stmt->bindParam(1, $id);
      $stmt->bindParam(2, $_FILES['file']['name']);
      $stmt->bindParam(3, filesize($file_tmp));
      $stmt->bindParam(4, $_FILES['file']['type']);
      $stmt->bindParam(5, $fp, PDO::PARAM_LOB);
      
      $conn->beginTransaction();
      $stmt->execute();
      $conn->commit();
      

      此示例打开一个文件并将文件句柄传递给 PDO 以将其作为 LOB 插入。 PDO 将尽最大努力以最有效的方式将文件内容上传到数据库。

      更多详情here

      【讨论】:

        猜你喜欢
        • 2017-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多