我必须使用php的mysql改进库将一行插入到mysql中具有类型为VARBINARY的主键的表中。该字段的内容是计算的sha1哈希。
如果我以旧的方式运行查询它完美地工作:
$mysqli->$query("INSERT INTO table (id, field1) VALUES (0x" . $id . ",'" . $field1 . "')");
但是当我尝试将其作为准备好的声明执行时,我无法弄清楚如何做到这一点。如果我执行等效操作:
if($stmt = $mysqli->prepare("INSERT INTO table (id, field1) VALUES (?, ?)")) {
$stmt->bind_param('ss', "0x".$id, $field1);
//execute statement
}
它会抛出一个异常,说明这个字段的内容太大了。如果我尝试将其作为BLOB字段插入:
if($stmt = $mysqli->prepare("INSERT INTO table (id, field1) VALUES (?, ?)")) {
$stmt->bind_param('bs', $id, $field1);
//execute statement
}
它没有给出错误,插入了行,但标识符字段现在为空(不为空,为空)。
我知道我可以混合查询并输入字符串中连接的id和其他字段作为预准备语句的绑定参数,但我只是想知道插入这个的正确方法是什么,也许它会帮助某些人未来。
PHP的sha1
函数返回十六进制数字的字符串表示。
这意味着如果你将它打印到屏幕上,它将显示一个十六进制数字。但在内存中,它是一堆ASCII字符。
所以,取十六进制数1A2F
。作为内存中的ASCII将是0x31413246
,而不是0x1A2F
MySQL的普通接口将所有参数作为字符串发送。使用普通接口时,MySQL会将ASCII字符串转换为二进制值。
新的预处理语句方法将所有内容都以二进制形因此,“1A2F”的漂亮值现在将作为0x31413246发送并插入到列中。 - source: dev.mysql.com - Prepared statements
相反,通过使用以下方法将Hex字符串打包成二进制字符串来转换它:
$binId = pack("H*", $id); // this string is not ASCII, don't print it to the screen! That will be ugly.
然后将$binId
传递给MySQLi预处理语句而不是$ id。
试试这个:
if($stmt = $mysqli->prepare("INSERT INTO table (id, field1) VALUES (unhex(?), ?)") {
$stmt->bind_param('ss', $id, $field1);
//execute statement
}
tl;博士:看看send_long_data()。
我知道这是一个非常古老的问题,但这正是我试图做的事情并且失败了。在尝试上述答案并花费大量时间进行实验之后,我终于发现了一些与我正在尝试的问题一致的方式。
这是令人困惑的,因为在引用超出允许的数据包大小的数据(我最初跳过的)时,间接引用如何在PHP bind_param documentation中继续使用“b”类型是唯一的:
如果变量的数据大小超过最大值。允许的数据包大小(max_allowed_packet),您必须在类型中指定b并使用mysqli_stmt_send_long_data()以数据包的形式发送数据。
事实证明,在执行插入之前必须自己发送二进制类型。我从article from Oracle's website发现了这个。
由于他们如此简洁地解释,我只想解释最相关的部分:
存储blob
这是使用MySQLi存储blob的代码:
$stmt = $mysqli->prepare("INSERT INTO images (image) VALUES(?)")
$null = NULL; //bolded
$stmt->bind_param("b", $null);
$stmt->send_long_data(0, file_get_contents("osaka.jpg")); //bolded
$stmt->execute();
我加粗了两段代码,我认为值得关注:
$ null变量是必需的,因为bind_param()总是需要给定参数的变量引用。在这种情况下,“b”(如blob中)参数。所以$ null只是一个虚拟,以使语法工作。
在下一步中,我需要用实际数据“填充”我的blob参数。这是由send_long_data()完成的。此方法的第一个参数指示将数据与哪个参数相关联。参数从0开始编号.send_long_data()的第二个参数包含要存储的实际数据。
在使用send_long_data()时,请确保blob不大于MySQL的max_allowed_packet