BLOB数据写入数据库失败

BLOB data fails to be written to a database

我创建了一个数据库,用于存储有关图像以及图像本身的各种数据。我有五个键 (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 变量来检查它的内容,并得到一长串数据垃圾,我认为这是在使用 [=23= 转换为 BLOB 期间生成的正确字符串] 函数。

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

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

选项 1:使用 send_long_data 方法传递二进制数据

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

Note:

If data size of a variable exceeds max. allowed packet size (max_allowed_packet), you have to specify b in types and use mysqli_stmt_send_long_data() to send the data in packets.

//...

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用于批量发送数据。当您的数据大小超过 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,您仍然必须以类似的方式处理流。

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

这避免了在这个问题中被问到的问题,使数据库更有效率,实际上加快了网页的传送速度。

把路径放在table中,这样在构建网页的时候就可以构建img标签了

因此您可以使用 PHP 流 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