核心功能
- 密码验证保护:上传与查看均需密码验证,保障内容安全
- 格式校验:支持JPG/PNG/GIF格式,自动校验文件类型
- 唯一标识:生成唯一ID命名文件,避免冲突
- 查看体验:支持图片缩放、拖拽,查看后自动删除保障隐私
使用步骤
- 在上传页面输入密码验证身份
- 选择本地图片文件,点击上传
- 上传成功后获取查看链接,分享或直接访问
- 查看页面支持鼠标滚轮缩放和拖拽浏览
技术特点
采用PHP实现服务端逻辑,包含:
- session会话管理确保验证状态
- finfo库进行MIME类型校验
- uniqid生成唯一文件ID
- 前端使用原生JS实现图片交互
项目展示
代码开源
upload.php - 上传处理核心
<?php
// upload.php 文件
session_start();
// 密码配置(用 password_hash() 生成的哈希值)
$passwordHash = '***'; // 生成命令:echo password_hash("你的密码", PASSWORD_DEFAULT)
// 密码验证逻辑
function checkAuth() {
global $passwordHash;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['password'])) {
// 验证密码提交
if (password_verify($_POST['password'], $passwordHash)) {
$_SESSION['authenticated'] = true;
header('Location: upload.php');
exit;
} else {
showLoginForm('密码错误');
}
}
if (!isset($_SESSION['authenticated']) || !$_SESSION['authenticated']) {
showLoginForm();
}
}
// 显示登录表单
function showLoginForm($error = '') {
$html = '<!DOCTYPE html>
<html>
<head>
<title>登录验证</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #f0f0f0;
}
.login-box {
background: white;
padding: 2rem;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 5px;
}
.error { color: red; }
</style>
</head>
<body>
<div class="login-box">
<h2>需要验证</h2>
<form method="post">
'.($error ? '<p class="error">'.$error.'</p>' : '').'
<input type="password" name="password" placeholder="输入访问密码" required>
<button type="submit">登录</button>
</form>
</div>
</body>
</html>';
die($html);
}
// 执行验证检查
checkAuth();
// 以下是原有的上传处理逻辑
// 上传处理逻辑
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
$uploadDir = 'uploads/';
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$file = $_FILES['image'];
// 验证上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
die("上传失败,错误代码:" . $file['error']);
}
// 验证文件类型
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif'
];
if (!isset($allowed[$mime])) {
die("仅支持 JPG, PNG 和 GIF 格式的图片。");
}
// 生成唯一ID和文件名
$id = uniqid('img_', true);
$extension = $allowed[$mime];
$filename = $id . '.' . $extension;
$destination = $uploadDir . $filename;
// 移动文件到上传目录
if (move_uploaded_file($file['tmp_name'], $destination)) {
$url = 'view.php?id=' . urlencode($id);
echo "上传成功!图片地址:<a href='$url' target='_blank'>$url</a>";
} else {
die("文件保存失败。");
}
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>图片上传</title>
</head>
<body>
<form method="post" enctype="multipart/form-data">
<input type="file" name="image" accept="image/*" required>
<input type="submit" value="上传">
</form>
</body>
</html>
view.php - 图片查看逻辑
<?php
// view.php 文件
$uploadDir = 'uploads/';
// 获取图片ID
$id = $_GET['id'] ?? null;
if (!$id) {
die("无效的请求。");
}
// 查找匹配的文件
$files = glob($uploadDir . $id . '.*');
if (empty($files)) {
die("图片已被人查看或删除。");
}
$filepath = $files[0];
$extension = pathinfo($filepath, PATHINFO_EXTENSION);
// 处理图片请求
if (isset($_GET['image'])) {
if (file_exists($filepath)) {
// 设置内容类型
$mimeTypes = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif'
];
header('Content-Type: ' . ($mimeTypes[$extension] ?? 'application/octet-stream'));
readfile($filepath);
unlink($filepath); // 删除文件
exit;
}
header("HTTP/1.0 404 Not Found");
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>查看图片</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #f0f0f0;
overflow: hidden;
}
.img-container {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
overflow: auto;
}
img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
transition: transform 0.1s ease;
cursor: zoom-in;
}
img.zoomed {
cursor: grab;
}
</style>
</head>
<body>
<div class="img-container">
<img
id="view-image"
src="view.php?id=<?= urlencode($id) ?>&image=1"
alt="上传的图片"
onload="initZoom()"
>
</div>
<script>
// 初始化缩放功能
function initZoom() {
const img = document.getElementById('view-image');
let scale = 1;
let isDragging = false;
let startX, startY, scrollLeft, scrollTop;
// 默认适应窗口大小
fitImageToWindow();
// 窗口大小变化时重新适应
window.addEventListener('resize', fitImageToWindow);
// 适应窗口大小
function fitImageToWindow() {
const container = document.querySelector('.img-container');
const imgAspectRatio = img.naturalWidth / img.naturalHeight;
const containerAspectRatio = container.clientWidth / container.clientHeight;
if (imgAspectRatio > containerAspectRatio) {
// 图片宽度优先
scale = container.clientWidth / img.naturalWidth;
} else {
// 图片高度优先
scale = container.clientHeight / img.naturalHeight;
}
img.style.transform = `scale(${scale})`;
img.classList.remove('zoomed');
}
// 鼠标滚轮缩放
img.addEventListener('wheel', (e) => {
e.preventDefault();
const delta = e.deltaY < 0 ? 1.1 : 0.9; // 滚轮向上放大,向下缩小
scale *= delta;
img.style.transform = `scale(${scale})`;
img.classList.toggle('zoomed', scale > 1);
});
// 触摸屏双指缩放
let touchStartDistance = 0;
img.addEventListener('touchstart', (e) => {
if (e.touches.length === 2) {
touchStartDistance = getDistance(e.touches[0], e.touches[1]);
}
});
img.addEventListener('touchmove', (e) => {
if (e.touches.length === 2) {
e.preventDefault();
const touchEndDistance = getDistance(e.touches[0], e.touches[1]);
scale = (touchEndDistance / touchStartDistance) * scale;
img.style.transform = `scale(${scale})`;
img.classList.toggle('zoomed', scale > 1);
}
});
// 拖拽功能
img.addEventListener('mousedown', (e) => {
if (scale > 1) {
isDragging = true;
startX = e.pageX - img.offsetLeft;
startY = e.pageY - img.offsetTop;
scrollLeft = img.scrollLeft;
scrollTop = img.scrollTop;
img.style.cursor = 'grabbing';
}
});
img.addEventListener('mousemove', (e) => {
if (isDragging) {
e.preventDefault();
const x = e.pageX - startX;
const y = e.pageY - startY;
img.scrollLeft = scrollLeft - x;
img.scrollTop = scrollTop - y;
}
});
img.addEventListener('mouseup', () => {
isDragging = false;
img.style.cursor = scale > 1 ? 'grab' : 'zoom-in';
});
img.addEventListener('mouseleave', () => {
isDragging = false;
img.style.cursor = scale > 1 ? 'grab' : 'zoom-in';
});
}
// 计算两点间距离
function getDistance(touch1, touch2) {
return Math.hypot(
touch2.pageX - touch1.pageX,
touch2.pageY - touch1.pageY
);
}
</script>
</body>
</html>