PHP实现上传图片保存到数据库的方法
PHP实现上传图片保存到数据库的方法
概述
本文将详细介绍如何通过PHP将图片直接保存到MySQL数据库中,这种方法特别适合多服务器环境下的文件共享需求,避免了在多台服务器间同步图片文件的麻烦。
为什么选择数据库存储图片?
- 多服务器共享:在多服务器环境中,数据库存储可以实现图片的即时共享
- 数据一致性:避免文件同步带来的不一致问题
- 备份简便:数据库备份会同时包含图片数据
- 权限管理:可以利用数据库的权限系统控制图片访问
MySQL BLOB类型选择
MySQL提供了四种BLOB类型用于存储二进制数据:
类型 | 容量限制 | 适用场景 |
---|---|---|
TinyBlob | 255字节 | 极小图标 |
Blob | 65KB | 小型图片 |
MediumBlob | 16MB | 大多数普通图片 |
LongBlob | 4GB | 超大图片 |
数据库表设计
CREATE TABLE `photo` (
`id` int(10) unsigned NOT NULL auto_increment,
`type` varchar(100) NOT NULL COMMENT '图片MIME类型',
`binarydata` mediumblob NOT NULL COMMENT '图片二进制数据',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`filename` varchar(255) DEFAULT NULL COMMENT '原始文件名',
`size` int(11) DEFAULT NULL COMMENT '文件大小(字节)',
PRIMARY KEY (`id`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1;
优化说明:
- 使用InnoDB引擎替代MyISAM,支持事务
- 添加了创建时间、原始文件名和文件大小字段
- 使用utf8mb4字符集支持更广的字符范围
- 添加了字段注释
完整实现代码
<?php
// 数据库配置
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'demo');
/**
* 获取数据库连接
*/
function getDbConnection() {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
return $conn;
}
// 判断action类型
$action = $_REQUEST['action'] ?? '';
// 上传图片
if ($action == 'add') {
// 验证文件上传是否成功
if ($_FILES['photo']['error'] !== UPLOAD_ERR_OK) {
die("文件上传失败,错误码: " . $_FILES['photo']['error']);
}
// 验证文件类型
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$type = $_FILES['photo']['type'];
if (!in_array($type, $allowedTypes)) {
die("只允许上传JPEG, PNG和GIF图片");
}
// 验证文件大小 (限制为5MB)
if ($_FILES['photo']['size'] > 5 * 1024 * 1024) {
die("图片大小不能超过5MB");
}
$conn = getDbConnection();
$stmt = $conn->prepare("INSERT INTO photo(type, binarydata, filename, size) VALUES (?, ?, ?, ?)");
$null = NULL;
$stmt->bind_param("sbss",
$type,
$null,
$_FILES['photo']['name'],
$_FILES['photo']['size']
);
// 使用send_long_data方法处理大文件
$fp = fopen($_FILES['photo']['tmp_name'], "rb");
while (!feof($fp)) {
$stmt->send_long_data(1, fread($fp, 8192));
}
fclose($fp);
$stmt->execute();
$stmt->close();
$conn->close();
header('Location: upload_image_todb.php');
exit();
// 显示图片
} elseif ($action == 'show') {
$id = $_GET['id'] ?? 0;
$id = (int)$id;
$conn = getDbConnection();
$stmt = $conn->prepare("SELECT type, binarydata FROM photo WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
$stmt->bind_result($type, $data);
$stmt->fetch();
header("Content-Type: " . $type);
echo $data;
} else {
header("HTTP/1.0 404 Not Found");
echo "图片不存在";
}
$stmt->close();
$conn->close();
exit();
// 显示图片列表及上传表单
} else {
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片上传到数据库演示</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.upload-form { background: #f5f5f5; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
.image-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 15px; }
.image-item { border: 1px solid #ddd; padding: 10px; border-radius: 5px; text-align: center; }
.image-item img { max-width: 100%; height: auto; }
</style>
</head>
<body>
<div class="upload-form">
<h2>上传图片</h2>
<form method="post" action="upload_image_todb.php" enctype="multipart/form-data">
<p>
<label for="photo">选择图片:</label>
<input type="file" name="photo" id="photo" accept="image/jpeg,image/png,image/gif" required>
</p>
<p>
<input type="hidden" name="action" value="add">
<button type="submit">上传图片</button>
</p>
</form>
</div>
<h2>图片列表</h2>
<div class="image-list">
<?php
$conn = getDbConnection();
$result = $conn->query("SELECT id, type, filename, size, created_at FROM photo ORDER BY id DESC");
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo '<div class="image-item">';
echo '<img src="upload_image_todb.php?action=show&id='.$row['id'].'&t='.time().'" alt="'.$row['filename'].'">';
echo '<p>'.$row['filename'].'</p>';
echo '<p>'.round($row['size']/1024, 1).' KB</p>';
echo '<p>'.date('Y-m-d H:i', strtotime($row['created_at'])).'</p>';
echo '</div>';
}
} else {
echo '<p>暂无图片</p>';
}
$conn->close();
?>
</div>
</body>
</html>
<?php
}
?>
要点
-
安全性增强:
- 使用预处理语句防止SQL注入
- 添加了文件类型检查
- 限制了文件大小
- 使用mysqli替代已废弃的mysql_函数
-
功能改进:
- 添加了文件信息存储(文件名、大小、上传时间)
- 使用流式处理大文件上传
- 改进的用户界面和响应式设计
- 添加了错误处理
-
性能优化:
- 使用InnoDB存储引擎
- 图片列表不加载二进制数据
- 添加了适当的索引
实际应用建议
- 缓存策略:对于频繁访问的图片,建议实现缓存机制
- CDN集成:高流量网站应考虑与CDN集成
- 分表策略:大量图片时,考虑按时间或分类分表
- 定期维护:设置定期清理过期图片的机制