
本文详解如何在 php + mysql 架构中,安全、规范地从数据库读取二进制文件(如 pdf、jpeg),并在独立页面(如后台管理页)触发浏览器下载,涵盖头信息设置、字符编码处理与常见陷阱规避。
本文详解如何在 php + mysql 架构中,安全、规范地从数据库读取二进制文件(如 pdf、jpeg),并在独立页面(如后台管理页)触发浏览器下载,涵盖头信息设置、字符编码处理与常见陷阱规避。
在 Web 应用中,常需将用户上传的文件(如合同 PDF、证件 JPG)存储于 MySQL 的 LONGBLOB 字段,并在后台管理页提供「下载」功能。关键在于:不能直接输出文件内容到 HTML 页面,而必须通过 HTTP 响应头强制触发浏览器下载行为。以下是一个生产就绪的实现方案。
✅ 核心步骤概览
- 通过唯一 ID(如 file_id)查询数据库,获取文件名与二进制内容;
- 设置正确的 HTTP 响应头,声明为附件下载;
- 输出原始二进制流,禁止任何额外输出(含空格、BOM、echo);
- 确保文件名支持中文/特殊字符(推荐 UTF-8 编码方案)。
? 完整可运行示例(download.php)
<?php
// download.php —— 接收 file_id 参数,执行下载
require_once 'db.php'; // 包含 PDO 连接配置
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
http_response_code(400);
die('Invalid file ID');
}
$id = (int)$_GET['id'];
try {
$pdo = getPDO(); // 假设 db.php 提供此函数
$stmt = $pdo->prepare("SELECT filename, content, mime_type FROM uploads WHERE id = ? AND status = 'active'");
$stmt->execute([$id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
http_response_code(404);
die('File not found or inaccessible');
}
$fileName = $row['filename']; // e.g. "报告_张三.pdf"
$fileContent = $row['content']; // LONGBLOB 数据
$mimeType = $row['mime_type'] ?: 'application/octet-stream';
// ✅ 关键:清除所有缓冲区,防止意外输出破坏二进制流
if (ob_get_level()) ob_end_clean();
// ✅ 设置标准下载响应头(兼容 IE / Chrome / Firefox)
header('Content-Description: File Transfer');
header('Content-Type: ' . $mimeType);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
// ✅ 安全处理非 ASCII 文件名(RFC 5987)
$encodedName = rawurlencode($fileName);
header("Content-Disposition: attachment; filename*=UTF-8''{$encodedName}");
header('Content-Length: ' . strlen($fileContent));
// ✅ 直接输出二进制内容(不经过 echo/print_r 等)
echo $fileContent;
exit;
} catch (PDOException $e) {
error_log('Download failed: ' . $e->getMessage());
http_response_code(500);
die('Server error');
}⚠️ 重要注意事项
- 绝不使用 filesize($fileContent):$fileContent 是字符串变量,不是文件路径;正确长度应为 strlen($fileContent) 或从数据库字段 OCTET_LENGTH(content) 获取;
- 避免 BOM 和空白符:确保 .php 文件以 UTF-8 无 BOM 编码保存,且文件开头无空行、空格或 <?php 前的任意字符;
- 数据库字段类型:务必使用 LONGBLOB(最大 4GB),而非 TEXT 或 VARCHAR,否则会损坏二进制数据;
-
安全性加固:
- 验证用户权限(如仅管理员可下载该文件);
- 过滤 $_GET['id'] 为整型,防止 SQL 注入;
- 添加 status 字段控制可见性,避免未审核文件被直接访问;
- 大文件优化建议:若文件普遍 >10MB,考虑改用 readfile() 流式输出(需先存临时文件)或 Nginx/Apache 的 X-Sendfile 头卸载 PHP 压力。
✅ 前端调用方式(Admin 页面)
<!-- 在 admin/list.php 中 --> <a href="download.php?id=123" class="btn btn-sm btn-primary">⬇ 下载《用户协议.pdf》</a>
? 提示:实际项目中,建议将下载逻辑封装为独立服务类(如 FileDownloader),并集成日志记录与下载统计,便于审计与运维。
通过以上方案,即可在任意页面(如管理员后台)安全、可靠地触发数据库中文件的下载,兼顾兼容性、安全性与可维护性。



















