【问题标题】:PHP- inserting binary data in mysql using prepared statementsPHP-使用准备好的语句在mysql中插入二进制数据
【发布时间】:2010-12-17 09:37:47
【问题描述】:

我必须使用 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 和其他字段作为准备好的语句的绑定参数,但我只是想知道插入它的正确方法是什么,也许它会将来帮助别人。

【问题讨论】:

  • 执行此操作时,$id 和 $field1 的值是多少? $id 是整数吗?如果是这样,您是否尝试过将其绑定? stmt->bind_param('is', $id, $field1);
  • $id 的值是一个 sha1 哈希。它不是整数。其他字段不会导致问题,但主要是字符串。
  • 另外一个用于在解决此问题的同时尝试保留准备好的语句的安全性。

标签: php mysql prepared-statement


【解决方案1】:

试试这个:

if($stmt = $mysqli->prepare("INSERT INTO table (id, field1) VALUES (unhex(?), ?)") {
    $stmt->bind_param('ss', $id, $field1);
    //execute statement
}

【讨论】:

    【解决方案2】:

    PHP 的 sha1 函数返回一个十六进制数字的字符串表示形式。

    这意味着如果您将其打印到屏幕上,它将显示一个十六进制数字。但是在内存中,是一堆ASCII字符。

    所以,取十六进制数1A2F。作为内存中的 ASCII,将是 0x31413246,而不是 0x1A2F

    MySQL 的普通接口将所有参数作为字符串发送。使用普通接口时,MySQL 会将 ASCII 字符串转换为二进制值。

    新的prepared statement方法以二进制形式发送所有内容。因此,“1A2F”的好值现在将作为 0x31413246 发送并插入列中。 - source: dev.mysql.com - Prepared statements

    相反,通过使用以下方法将十六进制字符串打包成二进制字符串:

    $binId = pack("H*", $id); // this string is not ASCII, don't print it to the screen! That will be ugly.
    

    然后将$binId 传递给 MySQLi 准备好的语句而不是 $id。

    【讨论】:

    • 你是对的。这是做到这一点的方法。非常感谢。
    • 我自己也在想这个!只是想注意,您还可以使用hex2bin 作为pack 的便捷替代品,并且在再次提取数据时同样可以使用bin2hex(而不必在您的语句中使用HEX())。跨度>
    【解决方案3】:

    tl;dr:查看send_long_data()

    我知道这是一个非常古老的问题,但它正是我试图做的并且失败了。在尝试了上述答案并花了很多时间进行实验之后,我终于找到了一些可以解决问题的方法,并且我正在尝试。

    这很令人困惑,因为在 PHP bind_param documentation 中如何处理 "b" 类型的唯一参考是间接引用超过允许数据包大小的数据(我最初跳过了):

    如果变量的数据大小超过最大值。允许的数据包大小 (max_allowed_pa​​cket),您必须在 types 中指定 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_pa​​cket

    【讨论】:

      猜你喜欢
      • 2017-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      • 2015-02-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多