我尝试了很多东西,但似乎没有简单的解决方案。
我的解决方案使用@yes123 提出的Location 标头技巧,但我已对其进行了调整以符合我的偏好。
文件的链接保持不变,所以它仍然是:/files/path/to/my/file.abc
我有一个RewriteRule:
RewriteRule ^files/(.*) path/to/tracker.php?path=/$1
然后在文件中,我通过在 URL 中添加 ?track=no 和之前的 RewriteRule 的例外来发出 Location 标头:
RewriteCond %{QUERY_STRING} !(&|^)track=no(&|$)
我又添加了一项优化。我已启用电子标签,因此如果客户端发送电子标签标头,请查看它是否与文件匹配并返回 304 Not Modified 而不是 Location。
$fs = stat($document_root . $path);
$apache_etag = calculate_apache_etag($fs);
if ((isset($_SERVER["HTTP_IF_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_MATCH"], $apache_etag))
|| (isset($_SERVER["HTTP_IF_NONE_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_NONE_MATCH"], $apache_etag))
) {
header("ETag: " . $apache_etag, true, 304);
exit;
}
function etag_within_range($etag1, $etag2) {
list($size1, $mtime1) = explode("-", $etag1);
list($size2, $mtime2) = explode("-", $etag2);
$mtime1 = floor(hexdec($mtime1) / 1000000);
$mtime2 = floor(hexdec($mtime2) / 1000000);
return $mtime1 === $mtime2 && $size1 === $size2;
}
calculate_apache_etag 的实现可以在这里找到:How do you make an etag that matches Apache?
etag_withing_range 解决了在 Apache 中与更高精度的mtime 进行比较的问题。
关于无效解决方案的说明
virtual
测试脚本:
var_dump(apache_response_headers());
virtual("/path/to/image.jpg");
var_dump(apache_response_headers());
输出:
array(1) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" }
[[binary junk]]
array(5) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" ["Keep-Alive"]=> string(18) "timeout=5, max=100" ["Connection"]=> string(10) "Keep-Alive" ["Transfer-Encoding"]=> string(7) "chunked" ["Content-Type"]=> string(9) "text/html" }
Content-Type: text/htmlreaaaaalllly? :(
也许PHP5.3的header_remove函数可以解决这个问题?我没试过。