Appearance
13.4 头像上传、图片展示
头像上传是用户系统中常见的功能,本章节将介绍如何实现头像上传和图片展示功能。
基本原理
头像上传的基本流程:
- 创建文件上传表单
- 验证上传的文件(类型、大小、是否为图片)
- 处理上传的文件(重命名、存储)
- 保存文件路径到数据库
- 展示上传的头像
实现头像上传
1. 创建上传表单
html
<!DOCTYPE html>
<html>
<head>
<title>头像上传</title>
<style>
body { font-family: Arial, sans-serif; max-width: 500px; margin: 0 auto; padding: 20px; }
h1 { text-align: center; color: #333; }
.avatar-container { text-align: center; margin: 20px 0; }
.avatar { width: 150px; height: 150px; border-radius: 50%; object-fit: cover; }
.form-group { margin: 15px 0; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input[type="file"] { padding: 10px; border: 1px solid #ddd; border-radius: 4px; width: 100%; box-sizing: border-box; }
input[type="submit"] { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
.message { padding: 10px; margin: 10px 0; border-radius: 4px; }
.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
</style>
</head>
<body>
<h1>头像上传</h1>
<div class="avatar-container">
<img src="<?php echo $avatarUrl; ?>" alt="头像" class="avatar">
</div>
<?php if (!empty($errors)): ?>
<div class="message error">
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if (isset($success)): ?>
<div class="message success">
<?php echo $success; ?>
</div>
<?php endif; ?>
<form action="" method="post" enctype="multipart/form-data">
<div class="form-group">
<label>选择头像: <input type="file" name="avatar" accept="image/*"></label>
</div>
<input type="submit" value="上传头像" name="submit">
</form>
</body>
</html>2. 服务器端处理
php
<?php
// 配置
$targetDir = "uploads/avatars/";
$maxFileSize = 2 * 1024 * 1024; // 2MB
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$errors = [];
$avatarUrl = 'default-avatar.png'; // 默认头像
// 确保上传目录存在
if (!is_dir($targetDir)) {
mkdir($targetDir, 0755, true);
}
// 模拟用户ID
$userId = 1;
// 检查是否已有头像
$userAvatar = $targetDir . "avatar_$userId.jpg";
if (file_exists($userAvatar)) {
$avatarUrl = $userAvatar;
} else {
// 检查其他格式
foreach ($allowedExtensions as $ext) {
$avatarFile = $targetDir . "avatar_$userId.$ext";
if (file_exists($avatarFile)) {
$avatarUrl = $avatarFile;
break;
}
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['avatar'])) {
$file = $_FILES['avatar'];
// 检查是否有错误
if ($file['error'] !== 0) {
$errors[] = '上传失败,错误代码: ' . $file['error'];
} else {
// 检查文件大小
if ($file['size'] > $maxFileSize) {
$errors[] = "文件太大,最大允许 2MB";
}
// 检查文件扩展名
$fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($fileExtension, $allowedExtensions)) {
$errors[] = "只允许上传图片文件(JPG、PNG、GIF、WebP)";
}
// 检查是否为真实图片
if (getimagesize($file['tmp_name']) === false) {
$errors[] = "请上传真实的图片文件";
}
// 处理上传
if (empty($errors)) {
// 生成新文件名
$newFileName = "avatar_$userId.$fileExtension";
$targetFile = $targetDir . $newFileName;
// 删除旧头像
foreach ($allowedExtensions as $ext) {
$oldAvatar = $targetDir . "avatar_$userId.$ext";
if (file_exists($oldAvatar)) {
unlink($oldAvatar);
}
}
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
$success = "头像上传成功!";
$avatarUrl = $targetFile;
} else {
$errors[] = "上传失败,请重试";
}
}
}
}
?>图片展示
1. 基本展示
php
<img src="<?php echo $avatarUrl; ?>" alt="头像" class="avatar">2. 响应式展示
html
<style>
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
}
@media (max-width: 768px) {
.avatar {
width: 80px;
height: 80px;
}
}
</style>
<img src="<?php echo $avatarUrl; ?>" alt="头像" class="avatar">3. 带占位符的展示
php
<?php
// 检查头像是否存在
if (file_exists($avatarUrl)) {
echo "<img src='$avatarUrl' alt='头像' class='avatar'>";
} else {
echo "<div class='avatar-placeholder'>
<span>用户</span>
</div>";
}
?>
<style>
.avatar-placeholder {
width: 100px;
height: 100px;
border-radius: 50%;
background-color: #e0e0e0;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
color: #9e9e9e;
}
</style>完整的头像上传系统
1. 数据库设计
sql
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
avatar VARCHAR(255) DEFAULT 'default-avatar.png',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);2. 上传处理
php
<?php
// upload_avatar.php
require_once 'db.php';
$conn = getDbConnection();
$targetDir = "uploads/avatars/";
$maxFileSize = 2 * 1024 * 1024; // 2MB
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$errors = [];
// 确保上传目录存在
if (!is_dir($targetDir)) {
mkdir($targetDir, 0755, true);
}
// 假设用户已登录,获取用户ID
$userId = 1; // 实际应用中从会话中获取
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['avatar'])) {
$file = $_FILES['avatar'];
// 检查是否有错误
if ($file['error'] !== 0) {
$errors[] = '上传失败,错误代码: ' . $file['error'];
} else {
// 检查文件大小
if ($file['size'] > $maxFileSize) {
$errors[] = "文件太大,最大允许 2MB";
}
// 检查文件扩展名
$fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($fileExtension, $allowedExtensions)) {
$errors[] = "只允许上传图片文件(JPG、PNG、GIF、WebP)";
}
// 检查是否为真实图片
if (getimagesize($file['tmp_name']) === false) {
$errors[] = "请上传真实的图片文件";
}
// 处理上传
if (empty($errors)) {
// 生成新文件名
$newFileName = uniqid() . '.' . $fileExtension;
$targetFile = $targetDir . $newFileName;
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
// 更新数据库
$stmt = $conn->prepare("UPDATE users SET avatar = ? WHERE id = ?");
$stmt->bind_param("si", $newFileName, $userId);
if ($stmt->execute()) {
header('Location: profile.php?success=头像上传成功');
exit;
} else {
$errors[] = "更新数据库失败";
}
$stmt->close();
} else {
$errors[] = "上传失败,请重试";
}
}
}
}
$conn->close();
?>3. 个人资料页面
php
<?php
// profile.php
require_once 'db.php';
$conn = getDbConnection();
// 假设用户已登录,获取用户ID
$userId = 1; // 实际应用中从会话中获取
// 获取用户信息
$stmt = $conn->prepare("SELECT username, email, avatar FROM users WHERE id = ?");
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
$avatarUrl = 'uploads/avatars/' . ($user['avatar'] ?: 'default-avatar.png');
if (!file_exists($avatarUrl)) {
$avatarUrl = 'default-avatar.png';
}
} else {
header('Location: login.php');
exit;
}
$stmt->close();
$conn->close();
?>
<!DOCTYPE html>
<html>
<head>
<title>个人资料</title>
<style>
body { font-family: Arial, sans-serif; max-width: 500px; margin: 0 auto; padding: 20px; }
h1 { text-align: center; color: #333; }
.profile { text-align: center; margin: 20px 0; }
.avatar { width: 150px; height: 150px; border-radius: 50%; object-fit: cover; }
.user-info { margin: 20px 0; }
.form-group { margin: 15px 0; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input[type="file"] { padding: 10px; border: 1px solid #ddd; border-radius: 4px; width: 100%; box-sizing: border-box; }
input[type="submit"] { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
.message { padding: 10px; margin: 10px 0; border-radius: 4px; }
.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
</style>
</head>
<body>
<h1>个人资料</h1>
<div class="profile">
<img src="<?php echo $avatarUrl; ?>" alt="头像" class="avatar">
<h2><?php echo $user['username']; ?></h2>
<p><?php echo $user['email']; ?></p>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="message success">
<?php echo $_GET['success']; ?>
</div>
<?php endif; ?>
<form action="upload_avatar.php" method="post" enctype="multipart/form-data">
<div class="form-group">
<label>更换头像: <input type="file" name="avatar" accept="image/*"></label>
</div>
<input type="submit" value="上传头像" name="submit">
</form>
</body>
</html>图片处理
1. 图片缩放
使用 GD 库对图片进行缩放:
php
<?php
// 缩放图片
function resizeImage($sourcePath, $targetPath, $width, $height) {
list($sourceWidth, $sourceHeight) = getimagesize($sourcePath);
$sourceImage = null;
switch (pathinfo($sourcePath, PATHINFO_EXTENSION)) {
case 'jpg':
case 'jpeg':
$sourceImage = imagecreatefromjpeg($sourcePath);
break;
case 'png':
$sourceImage = imagecreatefrompng($sourcePath);
break;
case 'gif':
$sourceImage = imagecreatefromgif($sourcePath);
break;
case 'webp':
$sourceImage = imagecreatefromwebp($sourcePath);
break;
}
if (!$sourceImage) {
return false;
}
$targetImage = imagecreatetruecolor($width, $height);
// 处理透明背景
if (pathinfo($sourcePath, PATHINFO_EXTENSION) === 'png') {
imagealphablending($targetImage, false);
imagesavealpha($targetImage, true);
$transparent = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
imagefilledrectangle($targetImage, 0, 0, $width, $height, $transparent);
}
imagecopyresampled($targetImage, $sourceImage, 0, 0, 0, 0, $width, $height, $sourceWidth, $sourceHeight);
$result = false;
switch (pathinfo($targetPath, PATHINFO_EXTENSION)) {
case 'jpg':
case 'jpeg':
$result = imagejpeg($targetImage, $targetPath, 90);
break;
case 'png':
$result = imagepng($targetImage, $targetPath, 9);
break;
case 'gif':
$result = imagegif($targetImage, $targetPath);
break;
case 'webp':
$result = imagewebp($targetImage, $targetPath, 90);
break;
}
imagedestroy($sourceImage);
imagedestroy($targetImage);
return $result;
}
// 使用示例
$sourcePath = $_FILES['avatar']['tmp_name'];
$targetPath = $targetDir . $newFileName;
$thumbnailPath = $targetDir . 'thumb_' . $newFileName;
// 保存原始图片
move_uploaded_file($sourcePath, $targetPath);
// 创建缩略图
resizeImage($targetPath, $thumbnailPath, 100, 100);
?>2. 图片裁剪
php
<?php
// 裁剪图片为正方形
function cropImage($sourcePath, $targetPath, $size) {
list($sourceWidth, $sourceHeight) = getimagesize($sourcePath);
// 计算裁剪区域
$cropSize = min($sourceWidth, $sourceHeight);
$cropX = ($sourceWidth - $cropSize) / 2;
$cropY = ($sourceHeight - $cropSize) / 2;
$sourceImage = null;
switch (pathinfo($sourcePath, PATHINFO_EXTENSION)) {
case 'jpg':
case 'jpeg':
$sourceImage = imagecreatefromjpeg($sourcePath);
break;
case 'png':
$sourceImage = imagecreatefrompng($sourcePath);
break;
case 'gif':
$sourceImage = imagecreatefromgif($sourcePath);
break;
case 'webp':
$sourceImage = imagecreatefromwebp($sourcePath);
break;
}
if (!$sourceImage) {
return false;
}
$targetImage = imagecreatetruecolor($size, $size);
// 处理透明背景
if (pathinfo($sourcePath, PATHINFO_EXTENSION) === 'png') {
imagealphablending($targetImage, false);
imagesavealpha($targetImage, true);
$transparent = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
imagefilledrectangle($targetImage, 0, 0, $size, $size, $transparent);
}
imagecopyresampled($targetImage, $sourceImage, 0, 0, $cropX, $cropY, $size, $size, $cropSize, $cropSize);
$result = false;
switch (pathinfo($targetPath, PATHINFO_EXTENSION)) {
case 'jpg':
case 'jpeg':
$result = imagejpeg($targetImage, $targetPath, 90);
break;
case 'png':
$result = imagepng($targetImage, $targetPath, 9);
break;
case 'gif':
$result = imagegif($targetImage, $targetPath);
break;
case 'webp':
$result = imagewebp($targetImage, $targetPath, 90);
break;
}
imagedestroy($sourceImage);
imagedestroy($targetImage);
return $result;
}
// 使用示例
cropImage($targetPath, $targetPath, 300); // 裁剪为 300x300 的正方形
?>注意事项
安全性:
- 验证上传文件的类型和大小
- 重命名文件,避免文件名冲突
- 限制上传目录的权限
- 不要将上传的文件直接存储在 web 根目录外
性能:
- 对上传的图片进行压缩和缩放
- 生成缩略图,提高加载速度
- 考虑使用 CDN 存储图片
用户体验:
- 提供清晰的上传提示
- 显示上传进度
- 提供图片预览功能
- 支持拖拽上传
兼容性:
- 支持不同浏览器的文件上传
- 考虑移动设备的上传体验
- 提供默认头像
练习
- 实现一个完整的头像上传系统
- 支持图片预览功能
- 实现图片缩放和裁剪
- 保存头像路径到数据库
- 在个人资料页面展示头像
