PHP项目二进制数据如何数据表存储读取

wen PHP项目 29

本文目录导读:

PHP项目二进制数据如何数据表存储读取

  1. 数据库字段类型选择
  2. PHP 写入二进制数据到数据库
  3. PHP 从数据库读取二进制数据并输出
  4. 读取后保存为文件(需要时)
  5. 重要注意事项
  6. 一个更完整的现代建议:文件系统 + 数据库路径

在 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/pngapplication/pdf),方便读取时正确设置 HTTP 响应头。


PHP 写入二进制数据到数据库

核心:使用预处理语句 + bind_paramb 类型

<?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
写入 使用预处理语句 bindParamPARAM_LOB,传入二进制字符串或文件流
读取 设置正确的 Content-Type,直接 echo 二进制数据到输出
性能 小文件可直接存;大文件建议存路径,避免 PHP 处理 IO

如果你的数据是超大文件(>10MB),强烈建议采用 文件系统存储 + 数据库记录路径 的模式,而不是直接存入 BLOB 字段。

抱歉,评论功能暂时关闭!