AttachmentDownloads 安装与使用说明
AttachmentDownloads 是面向 Typecho 1.3.0 的非图片附件下载保护与统计插件。插件将正文中的附件锚点运行时改写到下载网关,在通过验证码或短期授权后签发下载会话,并统计有效下载、每日 UV 和初始来源。

插件版权地址:https://www.144d.com。
功能概览
- 自动识别正文中的本地非图片附件链接,并改写为带随机公开键的下载入口。
- 提供关闭、每次验证、单附件授权窗口、全站授权窗口和风险触发五种验证码模式。
- 支持 PHP Stream、Nginx X-Accel-Redirect 和 Apache X-Sendfile 三种文件交付方式。
- 统计有效下载、每日 UV 和初始来源,提供趋势、排行、详情及 CSV 导出。
- 不记录原始 IP、完整 User-Agent、完整 Referer 或访客 Cookie 原值。
安装前准备
- Typecho 1.3.0 或更高版本。
- PHP 7.4 或更高版本。
- MySQL/MariaDB、PostgreSQL 或 SQLite。
- 正文自动改写需要 PHP DOM 扩展。
- 内置图片验证码需要 PHP GD 扩展。
- 默认
php_stream驱动需要 PHP 能读取上传目录。
安装前建议备份站点数据库和 usr/uploads/。插件启用时只创建独立数据表,不修改 Typecho 核心表结构,但源文件保护需要调整 Web 服务器配置。
安装
1. 安装并启用插件
- 将插件目录放到 Typecho 的
usr/plugins/下。 - 确认最终目录为
usr/plugins/AttachmentDownloads/,入口文件为usr/plugins/AttachmentDownloads/Plugin.php。目录名区分大小写,不能自行改名。 - 登录 Typecho 后台,进入“控制台 -> 插件”,找到“AttachmentDownloads”并启用。
- 启用成功后,后台会出现“下载统计”菜单。
如果启用失败,请先确认 PHP 版本、数据库建表权限和服务器错误日志。不要在插件目录名不正确时继续配置路由或服务器规则。
2. 完成首次配置
- 在插件列表中打开 AttachmentDownloads 设置。
- 核对“受保护下载扩展名”。默认值为
zip, rar, 7z, pdf, doc, docx, xls, xlsx, ppt, pptx, apk, txt, csv, epub。 - 选择验证码模式。默认“单附件授权窗口”会让同一访客完成验证后,在 15 分钟内重复下载同一附件时免于再次验证。
- 选择文件交付驱动,并确认“上传根目录”指向 Typecho 的真实上传目录。生产环境使用 Nginx 时推荐
x_accel;不确定时可先使用默认的php_stream。 - 保存设置后进入“下载统计 -> 设置与工具”,点击“开始重建索引”。按页面提示继续分批执行,直到全部附件扫描完成。
插件会自动同步启用后新增、修改或删除的 Typecho 附件。重建索引主要用于首次安装、迁移历史文章、批量导入附件或修复索引不一致。
3. 配置源文件保护
下载网关只能保护通过它发起的请求。要防止访客绕过验证码直接访问 /usr/uploads/...,还必须在 Web 服务器上拒绝受保护扩展名的公开静态访问。
Nginx
- 参考 nginx.conf.example,将配置人工合并到当前站点的
server块。 - 使用
x_accel时,将示例中的alias改为真实上传目录,并确保“内部 URI 前缀”与插件设置完全一致。 - Typecho 安装在子目录时,为内部路径和公开上传路径的
location同时加上对应的站点路径前缀。 - 让公开上传路径规则中的扩展名与插件的“受保护下载扩展名”保持一致,然后检查配置并平滑重载 Nginx。
Apache
- 参考 uploads.htaccess.example,将规则合并到真实的
usr/uploads/.htaccess。不要覆盖该目录已有规则。 - 使用默认
php_stream时,只需保证 PHP 对上传目录有读取权限。 - 使用
x_sendfile时,还需安装并启用mod_xsendfile,再参考 apache-xsendfile.conf.example 在 Apache 主配置或 VirtualHost 中设置XSendFilePath。 - 让
.htaccess中的扩展名与插件设置保持一致,然后检查配置并重载 Apache。
插件不会自动创建、覆盖或删除 Nginx、Apache 或 .htaccess 配置。只启用插件而不配置源文件保护,无法阻止访客绕过下载网关直接请求源文件。
4. 验证安装结果
- 打开“下载统计 -> 设置与工具”,执行“抽样检测直链保护”。
- 受保护的非图片源文件应返回
403或404;图片应保持正常访问。 - 打开一篇包含受保护附件的文章,确认附件链接进入
/download/.../下载入口。 - 完成验证码并下载文件,确认文件内容正常、Range 下载可用,并在“下载统计 -> 概览”中看到统计变化。
- 清理 Typecho 整页缓存和 CDN 页面缓存,使已有文章重新渲染为下载网关链接。验证码页、下载入口和文件响应都不应被 CDN 缓存。
使用说明
在文章中提供附件下载
文章作者继续使用 Typecho 原有的附件上传和插入方式即可,不需要短代码:
- 在编辑文章时上传附件,并把附件以普通
<a href="...">链接插入正文。 - 发布或预览文章时,插件会在运行时识别已索引且扩展名受保护的本地附件,并把链接改写到下载网关。
- 访客点击链接后按当前验证码策略完成验证。插件随后创建短期下载会话、启动文件下载,并自动返回附件所属文章。
图片会通过 MIME 和扩展名双重排除。插件只改写 HTML 中的 <a href>,不会处理图片、音视频 src、CSS URL 或由 JavaScript 动态生成的链接。
若附件使用已配置的 CDN 域名,请在“CDN 基础地址”中每行填写一个完整基础地址。未配置的外站链接不会被当作本站附件改写。
查看统计和复制下载入口
- “下载统计 -> 概览”:查看今日、近 7 天、近 30 天或自定义日期范围的下载、每日 UV、趋势、Top 10 和来源分布,并导出日统计 CSV。
- “下载统计 -> 附件排行”:按时间、文件名、扩展名和删除状态筛选附件,导出排行,或复制某个附件的公开下载入口。
- “下载统计 -> 附件详情”:查看单个附件的元数据、趋势和来源,复制或打开下载入口,重建该附件索引,导出 CSV,或按确认提示重置该附件统计。
- “下载统计 -> 设置与工具”:检查最终生效配置和服务器环境,继续重建附件索引,清理过期挑战、会话、限流及 UV 明细。
附件的公开下载入口可以放在正文之外,例如导航页或自定义主题页面。公开键只能降低通过 CID 枚举附件的风险,并不是登录权限或 DRM;拿到入口的访客仍可按当前验证策略下载文件。
关键设置
| 设置 | 作用与建议 |
|---|---|
| 受保护下载扩展名 | 决定哪些非图片附件会被改写和统计。修改后必须同步更新 Web 服务器的源文件拒绝规则。 |
| CDN 基础地址 | 每行一个允许参与匹配的 CDN 基础地址;普通外站链接不会被改写。 |
| 验证码模式 | “关闭”不显示验证码;“每次验证”每次都验证;“单附件授权窗口”仅复用同一附件授权;“全站授权窗口”在窗口期内复用全部附件授权;“风险触发”会对新访客、异常 UA、外部来源和直接访问要求验证。 |
| 验证授权窗口 | 控制“单附件授权窗口”和“全站授权窗口”的免重复验证时间,默认 15 分钟。 |
| 下载会话 | 控制下载令牌有效期和最多请求数。Range 分段与网络重试共享请求数上限。 |
| 会话绑定 | 可将短期下载会话绑定到 IP 网段哈希或浏览器标识哈希;启用前应考虑移动网络和代理切换。 |
| 统计排除 | 默认不统计管理员下载,便于站点维护时避免污染数据。 |
| 尊重 Do Not Track | 启用后不建立长期访客 Cookie;下载仍会统计,但跨浏览器会话的 UV 去重不再稳定。 |
| 文件交付驱动 | php_stream 兼容性最好;x_accel 适用于 Nginx;x_sendfile 适用于已启用 mod_xsendfile 的 Apache。 |
| 上传根目录 | 必须是服务器上的真实文件系统目录,而不是 URL。PHP 或发送模块需要具备读取权限。 |
| Nginx internal URI 前缀 | 仅供 x_accel 使用,必须与 Nginx internal location 完全一致。 |
交付驱动、上传根目录和 Nginx internal URI 前缀可以通过 config.inc.php 常量或同名环境变量覆盖。最终优先级为:常量、环境变量、插件设置、安全默认值。后台“设置与工具”会显示当前生效值及其来源。
默认行为
- 保护扩展名:
zip, rar, 7z, pdf, doc, docx, xls, xlsx, ppt, pptx, apk, txt, csv, epub。 - 图片通过 MIME 和扩展名双重排除,不改写、不统计。
- 兼容迁移文章中由
<span class="attachment">标记的旧附件;历史绝对域名只用于提取本地路径,普通外站链接不会被改写。 - 验证码模式:同一访客、同一附件验证后 15 分钟免重复验证。
- 验证码挑战有效期:5 分钟。
- 下载会话有效期:10 分钟,最多接受 32 个 GET/HEAD/Range 请求。
- 验证码挑战、失败尝试和下载会话创建同时按匿名访客及网络摘要限流。
- 同一下载会话只有首个有效 GET 计数;HEAD、非法 Range、后续 Range 和重试不重复计数。
- 下载会话创建后通过同源中转页启动文件下载,并自动返回附件所属文章;没有父文章时返回站点首页。
- 每日 UV:同一访客在站点时区的同一天内,对同一附件只计一次。
- UV 去重明细保留 90 天,日聚合和累计统计长期保留。
交付驱动
PHP Stream
默认安全回退,适用于本地 Apache 和未安装发送模块的环境。支持:
- 完整
200下载。 - 单段 Range
206。 - 越界或多段 Range
416。 - HEAD 元数据请求。
- 固定分块流式读取,不把整个附件载入内存。
Nginx X-Accel-Redirect
生产环境推荐使用。先将插件交付驱动改为 x_accel,再按 nginx.conf.example 配置内部目录。Nginx 的 alias 必须指向真实上传目录,内部 URI 前缀必须与插件最终生效值一致。
可在 config.inc.php 中使用部署覆盖,避免从本地复制数据库后误用 Windows 路径:
define('ATTACHMENT_DOWNLOAD_DELIVERY_DRIVER', 'x_accel');
define('ATTACHMENT_DOWNLOAD_UPLOAD_ROOT', '/var/www/typecho/usr/uploads');
define('ATTACHMENT_DOWNLOAD_INTERNAL_URI_PREFIX', '/_attachment_download_internal/');也可以使用同名环境变量。优先级为常量、环境变量、插件设置、安全默认值。
Apache X-Sendfile
安装 mod_xsendfile 后可使用 x_sendfile。按 apache-xsendfile.conf.example 允许真实上传目录。源文件拒绝规则仍需单独配置。
源文件保护
Nginx 模板同时包含:
- 仅能由
X-Accel-Redirect使用的internal路径。 - 对当前非图片下载扩展名返回
404的公开上传路径规则。
Apache 的 uploads.htaccess.example 应放到真实 usr/uploads/ 目录并按站点扩展名调整。插件不会自动创建、覆盖或删除任何服务器配置。
受保护扩展名与服务器规则必须保持一致。若把 mp3、mp4 等内嵌媒体加入保护集合,主题中的 <audio src>、<video src> 和 <source src> 将无法直接播放;后台环境检测会提示这类冲突。
后台功能
- 概览:今日、近 7 天、近 30 天、累计下载,每日 UV、趋势、Top 10 和来源分布。
- 附件排行:按周期、文件名、扩展名和删除状态筛选,查看周期下载、周期每日 UV 和累计下载。
- 附件详情:元数据、按日趋势、来源排行、公开下载入口、CSV 和单附件统计重置。
- 设置与工具:最终生效配置来源、环境检测、服务器规则、分批索引、短期数据清理和数据删除。
- CSV:导出日统计、附件排行和来源聚合,不导出访客哈希、网络哈希、挑战哈希或会话哈希。
主题 API
根据附件 CID 获取下载入口:
$url = \TypechoPlugin\AttachmentDownloads\Plugin::downloadUrl($attachmentCid);
if ($url !== null) {
echo htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
}获取累计下载量:
$downloads = \TypechoPlugin\AttachmentDownloads\Plugin::totalDownloads($attachmentCid);获取指定周期的下载次数和每日 UV 合计:
$period = \TypechoPlugin\AttachmentDownloads\Plugin::downloadPeriod($attachmentCid, '30d');获取安全元数据:
$metadata = \TypechoPlugin\AttachmentDownloads\Plugin::downloadMetadata($attachmentCid);元数据只包含 CID、文件名、扩展名、MIME、大小和累计下载,不返回物理路径或源文件 URL。
配合本站主题的效果:

数据与隐私
- 浏览器保存至少 192 bit 随机第一方 Cookie,设置
HttpOnly和SameSite=Lax,HTTPS 下同时设置Secure。 - 数据库只保存带插件密钥的 HMAC,不保存 Cookie 原值、原始 IP、完整 User-Agent 或完整 Referer。
- IP 网段和 User-Agent 仅在启用会话绑定或限流时以 HMAC 参与短期状态。
- 外部来源只保存规范化小写域名,站内来源优先保存内容 CID。
- 启用 Do Not Track 后,已有长期访客 Cookie 会降级为浏览器会话 Cookie;下载仍可统计,但跨浏览器会话 UV 不再保持稳定。
下载链接不继承文章密码权限,也不要求登录。随机公开键用于降低 CID 枚举风险,但不是 DRM 或用户权限系统;获得下载入口的人仍可通过验证下载附件。
停用与删除
- 停用插件会移除路由、Action、菜单和运行时钩子。插件使用独立配置备份规避 Typecho 停用时清理标准插件配置的行为,下次启用会恢复原设置。
- 停用不会移除 Nginx、Apache 或
.htaccess规则。服务器保护仍开启时,受保护附件会暂时无法下载。 - “清空全部统计”保留附件索引,只重置累计和聚合统计。
- “永久删除插件数据与配置”不可恢复;执行后应立即停用插件,停用过程会清除独立配置备份,重新启用会创建空表和默认配置。
永久卸载前应先恢复源文件公开规则,确认站点附件访问策略,再删除插件数据。
测试
测试使用临时 SQLite 文件,不读取或写入站点数据库:
php usr/plugins/AttachmentDownloads/tests/run.php覆盖范围包括建表、附件索引、跨数据库 upsert、DOM 链接改写、路径穿越与双重编码拒绝、每日 UV 去重、来源聚合、Range 和响应头安全。
故障排查
正文链接没有改写
确认 DOM 扩展已启用、附件索引已完成,并清理整页缓存。插件只改写 <a href>,不会改写图片、音视频 src、CSS URL 或脚本生成的链接。迁移文章若没有 Typecho 原生附件记录,需要保留 <span class="attachment"> 标记,并确保 URL 路径对应的文件存在于“上传根目录”下。
验证码显示失败
确认 GD 扩展已启用,并检查 /action/attachment-download?do=captcha 是否被缓存或安全规则拦截。验证码与下载响应必须保持 no-store。
提示“请求来源无效”
清理验证码页和 CDN 缓存后重新打开下载入口。插件使用当前请求的同源相对地址提交验证码,并同时校验浏览器 Fetch Metadata、当前请求源和 Typecho 站点地址;反向代理必须正确转发 Host 和 X-Forwarded-Proto。
Nginx 下载返回空内容或 404
核对 internal URI、alias、上传根目录和附件 URL 路径前缀。alias 应指向 usr/uploads/,并以斜杠结尾。
Apache 下载被源文件规则一起阻止
拒绝规则只影响 HTTP 静态访问,不应影响 PHP 文件读取。确认 PHP 用户对上传目录有读取权限;使用 X-Sendfile 时还要配置 XSendFilePath。
插件下载地址
其他
如果大家有什么其他插件需求,也可以留言,包括但不限于typecho等PHP博客。
评论
0成为第一个留下想法的人。