本文目录导读:

在 PHP 项目中,存储和读取二进制数据(如图片、文件、加密数据等)到数据库,主要涉及三个核心步骤:字段类型选择、数据写入、数据读取与输出。
下面以最常见的 MySQL 数据库为例,分步说明整个流程。
数据库字段类型选择
MySQL 提供了几种专门存储二进制数据的 BLOB(Binary Large Object)类型,根据数据大小选择:
| 类型 | 最大长度 | 适用场景 |
|---|---|---|
TINYBLOB |
255 字节 | 小图标、短签名 |
BLOB |
65,535 字节 (64KB) | 普通图片、小文件 |
MEDIUMBLOB |
16,777,215 字节 (16MB) | 中等文件、PDF |
LONGBLOB |
4,294,967,295 字节 (4GB) | 大文件、视频 |
建表示例(存储用户头像):
CREATE TABLE user_avatar (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
avatar_data MEDIUMBLOB NOT NULL, -- 二进制数据主体
mime_type VARCHAR(50) NOT NULL, -- 推荐额外存储文件类型
file_name VARCHAR(255), -- 原始文件名(可选)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
强烈建议:额外存储
mime_type(如image/png、application/pdf),方便读取时正确设置 HTTP 响应头。
PHP 写入二进制数据到数据库
核心:使用预处理语句 + bind_param 的 b 类型
<?php
// 假设用户上传了一个文件(来自表单或 API)
$filePath = 'path/to/avatar.png';
$userId = 123;
// 1. 读取文件为二进制字符串
$binaryData = file_get_contents($filePath);
if ($binaryData === false) {
die('文件读取失败');
}
// 2. 获取文件信息
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath);
finfo_close($finfo);
// 3. 连接数据库(使用 PDO 或 mysqli,这里用 PDO 示例)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 4. 使用预处理语句插入二进制数据
$sql = "INSERT INTO user_avatar (user_id, avatar_data, mime_type) VALUES (?, ?, ?)";
$stmt = $pdo->prepare($sql);
// 关键:绑定参数时,二进制数据用文件流或直接绑定字符串
// PDO 方法一:直接绑定字符串(适合小于 1MB 的数据)
$stmt->bindParam(1, $userId, PDO::PARAM_INT);
$stmt->bindParam(2, $binaryData, PDO::PARAM_LOB); // 对于二进制数据,使用 PARAM_LOB
$stmt->bindParam(3, $mimeType, PDO::PARAM_STR);
$stmt->execute();
// PDO 方法二:使用文件流(适合大文件,节省内存)
// $fileStream = fopen($filePath, 'rb');
// $stmt->bindParam(2, $fileStream, PDO::PARAM_LOB);
// $stmt->execute();
// fclose($fileStream);
echo "写入成功,ID: " . $pdo->lastInsertId();
?>
如果是 MySQLi 扩展:
$mysqli = new mysqli('localhost', 'root', 'password', 'test');
$stmt = $mysqli->prepare("INSERT INTO user_avatar (user_id, avatar_data, mime_type) VALUES (?, ?, ?)");
$null = null; // 占位
$stmt->bind_param('ibs', $userId, $null, $mimeType);
// 使用 send_long_data 发送大数据
$stmt->send_long_data(1, $binaryData);
$stmt->execute();
PHP 从数据库读取二进制数据并输出
最常用场景:根据用户请求直接输出图片/文件
<?php
// 假设通过 URL 参数获取用户 ID: display_avatar.php?user_id=123
$userId = (int)($_GET['user_id'] ?? 0);
if ($userId <= 0) {
header('HTTP/1.1 400 Bad Request');
exit;
}
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', 'password');
$sql = "SELECT avatar_data, mime_type FROM user_avatar WHERE user_id = ? ORDER BY id DESC LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute([$userId]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
header('HTTP/1.1 404 Not Found');
exit;
}
// 设置正确的 Content-Type
header('Content-Type: ' . $row['mime_type']);
// 可选:设置缓存策略
header('Cache-Control: public, max-age=86400');
header('Content-Length: ' . strlen($row['avatar_data']));
// 输出二进制数据
echo $row['avatar_data']; // 直接输出流到浏览器
exit;
?>
访问方式:在 HTML 中 <img src="display_avatar.php?user_id=123">。
读取后保存为文件(需要时)
// 从数据库读取后保存到本地文件 $binaryData = $row['avatar_data']; $outputFile = 'downloaded_avatar.' . pathinfo($row['file_name'], PATHINFO_EXTENSION); file_put_contents($outputFile, $binaryData);
重要注意事项
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 内存占用 | 读取大文件(>100MB)到 PHP 变量可能导致内存溢出 | 使用 PDO::PARAM_LOB + 文件流读写,或考虑 将文件存于文件系统,数据库只存路径 |
| SQL 语句长度限制 | 某些 MySQL 客户端限制 max_allowed_packet(默认 4MB) |
调整 max_allowed_packet 值,或拆分数据 |
| Base64 编码陷阱 | 很多人习惯将二进制数据先 base64_encode 再存为 TEXT | ❌ 错误做法:数据膨胀 33%,查询变慢,索引失效;✅ 应直接存 BLOB |
| 事务处理 | 写入大 BLOB 时建议使用事务或分段写入 | 确保原子性,避免损坏 |
一个更完整的现代建议:文件系统 + 数据库路径
对于大多数 Web 项目,不建议二进制数据直接存入数据库(除极特殊情况),推荐方案:
-- 数据库只存文件路径
CREATE TABLE user_files (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
file_path VARCHAR(500) NOT NULL, -- 存储相对路径,如 /uploads/avatars/123.png
mime_type VARCHAR(50),
file_size BIGINT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
优点:
- 数据库备份体积小,性能好
- 文件可直接由 Nginx/Apache 服务,无需 PHP 处理
- 支持 CDN、分布式存储(S3、OSS 等)
何时仍用数据库存储二进制?
- 数据必须与数据库事务强一致(如加密数据、法律凭证附件)
- 文件很小(< 100KB)且数量巨大
- 需要数据库级别的加密、压缩或列级权限控制
| 操作 | 要点 |
|---|---|
| 建表 | 用 MEDIUMBLOB / LONGBLOB,额外存 mime_type |
| 写入 | 使用预处理语句 bindParam 的 PARAM_LOB,传入二进制字符串或文件流 |
| 读取 | 设置正确的 Content-Type,直接 echo 二进制数据到输出 |
| 性能 | 小文件可直接存;大文件建议存路径,避免 PHP 处理 IO |
如果你的数据是超大文件(>10MB),强烈建议采用 文件系统存储 + 数据库记录路径 的模式,而不是直接存入 BLOB 字段。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播