59 lines
1.9 KiB
PHP
59 lines
1.9 KiB
PHP
<?php
|
|||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
const WEB_MEDIA_ROOT = '/opt/im-server/media';
|
||
|
|
|
||
|
|
function failDownload(int $status, string $message): never
|
||
|
|
{
|
||
|
|
http_response_code($status);
|
||
|
|
header('Content-Type: text/plain; charset=utf-8');
|
||
|
|
echo $message;
|
||
|
|
exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
function safeDownloadName(string $fileName, string $fallback): string
|
||
|
|
{
|
||
|
|
$name = basename(str_replace('\\', '/', $fileName));
|
||
|
|
$name = preg_replace('/[^\p{L}\p{N}._ -]+/u', '_', $name) ?: '';
|
||
|
|
$name = trim($name, " .\t\n\r\0\x0B");
|
||
|
|
if ($name === '') {
|
||
|
|
$name = $fallback;
|
||
|
|
}
|
||
|
|
|
||
|
|
return mb_substr($name, 0, 180, 'UTF-8');
|
||
|
|
}
|
||
|
|
|
||
|
|
$url = trim((string)($_GET['url'] ?? ''));
|
||
|
|
$requestedName = trim((string)($_GET['name'] ?? ''));
|
||
|
|
if ($url === '') {
|
||
|
|
failDownload(400, '缺少媒体地址');
|
||
|
|
}
|
||
|
|
|
||
|
|
$path = (string)(parse_url($url, PHP_URL_PATH) ?: $url);
|
||
|
|
$path = rawurldecode($path);
|
||
|
|
if (!preg_match('#^/fs/[0-9]+/[A-Za-z0-9_./ -]+$#', $path) || str_contains($path, '..')) {
|
||
|
|
failDownload(400, '媒体地址不合法');
|
||
|
|
}
|
||
|
|
|
||
|
|
$root = realpath(WEB_MEDIA_ROOT);
|
||
|
|
$fullPath = realpath(rtrim(WEB_MEDIA_ROOT, '/') . $path);
|
||
|
|
if ($root === false || $fullPath === false || !str_starts_with($fullPath, $root . DIRECTORY_SEPARATOR) || !is_file($fullPath)) {
|
||
|
|
failDownload(404, '媒体文件不存在');
|
||
|
|
}
|
||
|
|
|
||
|
|
$fallbackName = basename($fullPath);
|
||
|
|
$downloadName = safeDownloadName($requestedName, $fallbackName);
|
||
|
|
if (!pathinfo($downloadName, PATHINFO_EXTENSION)) {
|
||
|
|
$downloadName .= '.' . (pathinfo($fallbackName, PATHINFO_EXTENSION) ?: 'jpg');
|
||
|
|
}
|
||
|
|
|
||
|
|
$mime = mime_content_type($fullPath) ?: 'application/octet-stream';
|
||
|
|
header('Content-Type: ' . $mime);
|
||
|
|
header('Content-Length: ' . (string)filesize($fullPath));
|
||
|
|
header(
|
||
|
|
'Content-Disposition: attachment; filename="' . addcslashes($downloadName, "\\\"") . '"; filename*=UTF-8\'\''
|
||
|
|
. rawurlencode($downloadName)
|
||
|
|
);
|
||
|
|
header('Cache-Control: no-store');
|
||
|
|
readfile($fullPath);
|