可行但需用BLOB字段+PDO::PARAM_LOB绑定,禁用字符串拼接;读取用fpassthru避免内存溢出;小文件可选Redis Hash;大文件建议对象存储存路径。

PHP 直接往数据库存二进制数据(比如图片、PDF、ZIP)是可行的,但必须用 BLOB 字段 + 正确的绑定方式,否则容易出现截断、乱码、空值或 PDO 报错 SQLSTATE[HY000]: General error: 2006 MySQL server has gone away。别用 mysql_* 函数或字符串拼接 INSERT,那会出安全和兼容问题。
用 PDO::PARAM_LOB 绑定文件内容,不是字符串拼接
常见错误是把 file_get_contents() 结果直接拼进 SQL 字符串里,这既不安全又会因特殊字节(如 \x00)导致插入失败或被截断。正确做法是使用预处理语句 + PDO::PARAM_LOB 类型绑定。
-
PDO::PARAM_LOB告诉 PDO:这个参数是大对象,要以流式方式传输,不经过字符编码转换 - 字段类型必须是
TINYBLOB/BLOB/MEDIUMBLOB/LONGBLOB,不能是TEXT或VARCHAR - MySQL 连接需设置
PDO::ATTR_EMULATE_PREPARES => false,否则PARAM_LOB可能失效 - 示例关键代码:
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
]);
$stmt = $pdo->prepare("INSERT INTO files (original_name, mime_type, content) VALUES (?, ?, ?)");
$stmt->bindValue(1, $_FILES['file']['name'], PDO::PARAM_STR);
$stmt->bindValue(2, $_FILES['file']['type'], PDO::PARAM_STR);
$stmt->bindValue(3, fopen($_FILES['file']['tmp_name'], 'rb'), PDO::PARAM_LOB);
$stmt->execute();
读取 BLOB 时用 fpassthru() 输出,避免内存溢出
从数据库读出大文件(比如 50MB 的 PDF)再用 echo 或 print 输出,会一次性加载进 PHP 内存,极易触发 Fatal error: Allowed memory size exhausted。正确做法是让数据库结果集直接流式输出到响应体。
- 查询时用
PDO::FETCH_ASSOC获取记录,但不要用$row['content']赋值给变量 - 对
content字段调用fopen('php://memory', 'rwb')并写入,或更简单:直接用fpassthru()配合oci_fetch_lob()(Oracle)或原生 MySQLi 的mysqli_stmt_bind_result()+stream_get_contents() - 必须设置正确的 HTTP 头:
Content-Type和Content-Disposition: attachment; filename="xxx" - 记得在输出前调用
ob_end_clean()清除输出缓冲,防止 header 冲突
Redis Hash 存二进制数据,比数据库更轻量且无需编码
如果只是临时缓存或用户会话级的小文件(如头像缩略图、序列化配置),用 Redis Hash 更快、更省事。它原生支持二进制安全,hset 的 value 可直接传 raw bytes,不用 Base64。
立即学习“PHP免费学习笔记(深入)”;
- phpredis 扩展下,
$redis->hSet('user:123', 'avatar', file_get_contents($tmp))是合法且高效的 - 字段名(field)也支持二进制,但建议只用于值(value),字段名保持可读性
- 注意 Redis 单 key 默认上限是 512MB,超大文件仍应走文件系统 + 数据库存路径
- 不适用于需要事务、备份一致性或复杂查询的场景——Redis 没有 WHERE 条件查 BLOB 内容的能力
用 igbinary 序列化数组再存 BLOB,比 serialize 省 30%–50% 空间
如果你存的是 PHP 数组(比如用户导出的报表数据、表单快照),别用 serialize() 后扔进 BLOB。它体积大、解析慢、且含明文结构,有信息泄露风险。
- 确认已启用
igbinary扩展:php -m | grep igbinary - 存:用
igbinary_serialize($data)得到紧凑二进制字符串,再按前述PARAM_LOB方式入库 - 取:用
igbinary_unserialize($blob)解析,速度比原生unserialize()快约 2 倍 - 注意:igbinary 格式与 PHP 版本强绑定,跨大版本(如 8.1 → 8.3)可能不兼容,生产环境需统一运行时
真正难的不是“怎么存”,而是决定“该不该存”——BLOB 字段会让备份变慢、主从同步延迟升高、mysqldump 生成巨大 SQL 文件。超过 1MB 的文件,优先考虑对象存储(如 MinIO、S3)+ 数据库存路径,BLOB 只适合元数据强耦合、事务强一致、且体积可控(



















