本文目录导读:

在PHP中实现文件预览,通常会根据文件类型采用不同的方案,以下是几种常见的实现方式:
图片预览
<?php
// image_preview.php
$file = $_GET['file'] ?? '';
// 安全校验:防止路径遍历
$allowedPath = '/path/to/uploads/';
$realPath = realpath($allowedPath . $file);
if ($realPath && strpos($realPath, $allowedPath) === 0) {
$fileInfo = getimagesize($realPath);
header('Content-Type: ' . $fileInfo['mime']);
header('Content-Length: ' . filesize($realPath));
readfile($realPath);
} else {
http_response_code(404);
echo 'File not found';
}
PDF 预览
<?php
// pdf_preview.php
$file = $_GET['file'] ?? '';
// 安全校验
$filePath = '/path/to/pdfs/' . basename($file);
if (file_exists($filePath)) {
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($file) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
} else {
http_response_code(404);
echo 'PDF not found';
}
文本文件预览
<?php
// text_preview.php
$file = $_GET['file'] ?? '';
$filePath = '/path/to/texts/' . basename($file);
if (file_exists($filePath)) {
$content = file_get_contents($filePath);
// 检测编码
$encoding = mb_detect_encoding($content, ['UTF-8', 'GB2312', 'GBK'], true);
if ($encoding != 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $encoding);
}
header('Content-Type: text/plain; charset=utf-8');
echo nl2br(htmlspecialchars($content));
} else {
http_response_code(404);
echo 'File not found';
}
Office 文档预览(使用在线预览服务)
<?php
// office_preview.php
// 方式1:使用 Microsoft Office Online
$file = $_GET['file'] ?? '';
$fileUrl = 'https://yourdomain.com/uploads/' . urlencode($file);
$previewUrl = 'https://view.officeapps.live.com/op/view.aspx?src=' . urlencode($fileUrl);
header('Location: ' . $previewUrl);
exit;
// 方式2:使用 Google Docs Viewer
// $previewUrl = 'https://docs.google.com/viewer?url=' . urlencode($fileUrl) . '&embedded=true';
?>
<!DOCTYPE html>
<html>
<head>Office 文件预览</title>
</head>
<body>
<iframe src="https://view.officeapps.live.com/op/embed.aspx?src=<?php echo urlencode($fileUrl); ?>"
style="width:100%; height:600px;" frameborder="0">
</iframe>
</body>
</html>
视频文件预览
<?php
// video_preview.php
$file = $_GET['file'] ?? '';
$filePath = '/path/to/videos/' . basename($file);
if (file_exists($filePath)) {
// 支持 Range 请求,允许拖动进度条
$size = filesize($filePath);
$fp = fopen($filePath, 'rb');
header('Content-Type: video/mp4');
header('Accept-Ranges: bytes');
if (isset($_SERVER['HTTP_RANGE'])) {
// 处理分段请求
preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches);
$start = intval($matches[1]);
$end = isset($matches[2]) && $matches[2] !== '' ? intval($matches[2]) : $size - 1;
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $start-$end/$size");
header('Content-Length: ' . ($end - $start + 1));
fseek($fp, $start);
$chunkSize = 8192;
while ($start <= $end) {
$data = fread($fp, min($chunkSize, $end - $start + 1));
echo $data;
$start += $chunkSize;
}
} else {
header('Content-Length: ' . $size);
while (!feof($fp)) {
echo fread($fp, 8192);
flush();
}
}
fclose($fp);
} else {
http_response_code(404);
echo 'Video not found';
}
通用文件预览(自动识别类型)
<?php
// universal_preview.php
class FilePreviewer {
private $allowedPaths = [
'images' => ['jpg', 'jpeg', 'png', 'gif', 'webp'],
'pdf' => ['pdf'],
'text' => ['txt', 'md', 'log', 'csv', 'xml', 'html'],
'audio' => ['mp3', 'wav', 'ogg'],
'video' => ['mp4', 'webm', 'ogg']
];
public function preview($file, $uploadDir) {
// 安全检查
$file = basename($file); // 移除路径部分
$filePath = $uploadDir . '/' . $file;
if (!file_exists($filePath)) {
return ['error' => '文件不存在'];
}
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
$mimeType = mime_content_type($filePath);
// 根据文件类型返回合适的预览结果
if (in_array($extension, $this->allowedPaths['images'])) {
return $this->imagePreview($filePath, $mimeType);
} elseif (in_array($extension, $this->allowedPaths['pdf'])) {
return $this->pdfPreview($filePath);
} elseif (in_array($extension, $this->allowedPaths['text'])) {
return $this->textPreview($filePath);
} elseif (in_array($extension, $this->allowedPaths['audio'])) {
return $this->audioPreview($filePath);
} elseif (in_array($extension, $this->allowedPaths['video'])) {
return $this->videoPreview($filePath);
} else {
return $this->downloadPreview($filePath);
}
}
private function imagePreview($filePath, $mimeType) {
header('Content-Type: ' . $mimeType);
readfile($filePath);
return ['type' => 'image', 'content' => ''];
}
private function pdfPreview($filePath) {
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($filePath) . '"');
readfile($filePath);
return ['type' => 'pdf', 'content' => ''];
}
private function textPreview($filePath) {
$content = file_get_contents($filePath);
$encoding = mb_detect_encoding($content, ['UTF-8', 'GB2312', 'GBK'], true);
if ($encoding != 'UTF-8') {
$content = mb_convert_encoding($content, 'UTF-8', $encoding);
}
header('Content-Type: text/plain; charset=utf-8');
echo '<pre>' . htmlspecialchars($content) . '</pre>';
return ['type' => 'text', 'content' => $content];
}
private function downloadPreview($filePath) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
readfile($filePath);
return ['type' => 'download'];
}
}
// 使用示例
$previewer = new FilePreviewer();
$result = $previewer->preview($_GET['file'] ?? '', '/path/to/uploads');
?>
前端配合使用
<!DOCTYPE html>
<html>
<head>文件预览系统</title>
<style>
.preview-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.file-item {
display: inline-block;
margin: 10px;
padding: 15px;
border: 1px solid #ddd;
cursor: pointer;
}
.file-item:hover {
background-color: #f5f5f5;
}
.file-icon {
font-size: 48px;
text-align: center;
}
</style>
</head>
<body>
<div class="preview-container">
<h2>文件列表</h2>
<div id="fileList">
<?php
// 显示文件列表,点击进行预览
$files = glob('/path/to/uploads/*.*');
foreach ($files as $file) {
$filename = basename($file);
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
// 根据扩展名显示不同图标
$icon = '📄';
if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) $icon = '🖼️';
elseif ($extension == 'pdf') $icon = '📕';
elseif (in_array($extension, ['mp4', 'webm'])) $icon = '🎥';
elseif (in_array($extension, ['mp3', 'wav'])) $icon = '🎵';
echo "<div class='file-item' onclick=\"previewFile('" . $filename . "')\">";
echo "<div class='file-icon'>" . $icon . "</div>";
echo "<div>" . $filename . "</div>";
echo "</div>";
}
?>
</div>
<div id="previewArea" style="margin-top: 20px; display: none;">
<iframe id="previewFrame" style="width: 100%; height: 600px; border: 1px solid #ddd;"></iframe>
</div>
</div>
<script>
function previewFile(filename) {
var previewArea = document.getElementById('previewArea');
var previewFrame = document.getElementById('previewFrame');
// 根据文件类型决定预览方式
previewArea.style.display = 'block';
previewFrame.src = '/preview.php?file=' + encodeURIComponent(filename);
}
</script>
</body>
</html>
安全注意事项
<?php
// security_checks.php
/**
* 文件预览安全问题
*/
class SecurityChecks {
// 1. 路径遍历防护
public function preventPathTraversal($file, $uploadDir) {
$file = basename($file); // 移除所有路径部分
$realPath = realpath($uploadDir . '/' . $file);
$realUploadDir = realpath($uploadDir);
// 确保文件在允许的目录内
if ($realPath === false || strpos($realPath, $realUploadDir) !== 0) {
return false;
}
return $file;
}
// 2. 文件类型验证
public function validateFileType($filePath) {
$allowedTypes = ['image/jpeg', 'image/png', 'application/pdf', 'text/plain'];
$mimeType = mime_content_type($filePath);
return in_array($mimeType, $allowedTypes);
}
// 3. 文件大小限制
public function checkFileSize($filePath, $maxSize = 10485760) { // 10MB default
return filesize($filePath) <= $maxSize;
}
// 4. 用户权限验证
public function checkUserPermission($filePath) {
// 实现你的权限验证逻辑
// 例如检查数据库中的文件所有权
return true;
}
}
// 综合使用
$file = $_GET['file'] ?? '';
$uploadDir = '/var/www/uploads/';
$security = new SecurityChecks();
$safeFile = $security->preventPathTraversal($file, $uploadDir);
if ($safeFile && $security->validateFileType($uploadDir . '/' . $safeFile) && $security->checkUserPermission($safeFile)) {
// 执行预览
} else {
http_response_code(403);
echo 'Access denied';
}
最佳实践建议:
- 安全优先:始终验证文件路径、类型和用户权限
- 性能优化:对大文件使用流式传输,避免一次性加载到内存
- 缓存策略:对不经常变化的文件设置缓存头,提高性能
- 错误处理:提供清晰的错误信息,但不要泄露服务器路径
- 前端适配:根据设备类型(桌面/移动)适配预览界面
选择哪种方案取决于你的具体需求、文件类型和服务器环境,对于复杂的 Office 文档,推荐使用微软或 Google 的在线预览服务。