使用PHP下载远程文件至服务器并输出至浏览器,实现缓存省带宽
Got it, let's solve this caching problem for your PHP file download script. The core goal is to avoid redundant remote downloads when multiple users request the same file, while still serving fresh content and handling concurrency properly. Here's a robust, production-ready approach:
PHP File Download with Server-Side Caching
Full Implementation Code
<?php // Configuration $cacheDir = __DIR__ . '/file_cache/'; // Cache storage directory $cacheTTL = 86400; // Cache validity in seconds (1 day) $remoteUrl = $_GET['url'] ?? ''; // Get remote URL from request (sanitize this in production!) // Sanitize input (critical for security!) if (empty($remoteUrl) || !filter_var($remoteUrl, FILTER_VALIDATE_URL)) { http_response_code(400); echo "Invalid or missing URL"; exit; } // Create cache directory if it doesn't exist if (!is_dir($cacheDir)) { mkdir($cacheDir, 0755, true); } // Generate unique cache filename (based on remote URL hash) $cacheKey = md5($remoteUrl); $cacheFile = $cacheDir . $cacheKey; $lockFile = $cacheFile . '.lock'; // Function to wait for lock release (handle concurrency) function waitForLock($lockFile, $maxAttempts = 10, $delay = 1) { $attempts = 0; while (file_exists($lockFile) && $attempts < $maxAttempts) { sleep($delay); $attempts++; } return !file_exists($lockFile); } // Check if cache is valid $cacheIsValid = file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTTL); if (!$cacheIsValid) { // Handle concurrent requests: wait for existing download to finish if (!waitForLock($lockFile)) { http_response_code(503); echo "Too many concurrent requests. Please try again later."; exit; } // Create lock file to block other requests touch($lockFile); try { // Download remote file to cache $remoteContent = file_get_contents($remoteUrl); if ($remoteContent === false) { throw new Exception("Failed to download remote file"); } // Save to cache file_put_contents($cacheFile, $remoteContent); } catch (Exception $e) { // Clean up on failure if (file_exists($cacheFile)) unlink($cacheFile); http_response_code(500); echo "Error: " . $e->getMessage(); exit; } finally { // Release lock if (file_exists($lockFile)) unlink($lockFile); } } // Get file info for proper headers $fileSize = filesize($cacheFile); $contentType = mime_content_type($cacheFile); // Set headers to output file to user header("Content-Type: $contentType"); header("Content-Length: $fileSize"); header("Content-Disposition: inline; filename=\"" . basename(parse_url($remoteUrl, PHP_URL_PATH)) . "\""); header("Cache-Control: public, max-age=3600"); // Client-side cache // Output the cached file readfile($cacheFile); exit; ?>
Key Features Explained
- Secure Input Handling: Uses
filter_varto validate the remote URL, preventing path traversal or invalid requests. - Concurrency Control: Uses a lock file to ensure only one request downloads the remote file at a time—other requests wait for the cache to be ready instead of starting duplicate downloads.
- TTL-Based Caching: The
$cacheTTLvariable lets you control how long cached files stay valid (adjust based on how often the remote file updates). - Proper Header Management: Sends correct
Content-Type,Content-Length, andContent-Dispositionheaders so browsers handle the file correctly (inline download or attachment). - Error Handling: Cleans up incomplete cache files and lock files if the remote download fails, avoiding corrupted cache entries.
Important Notes
- Permissions: Ensure the
file_cachedirectory is writable by your web server user (e.g.,www-dataon Apache). - Cache Cleanup: Add a cron job to delete old cache files periodically to save disk space. For example:
# Delete cache files older than 7 days find /path/to/file_cache -type f -mtime +7 -delete - Advanced Cache Validation: For files that update frequently, you could extend this to check the remote server's
Last-Modifiedheader and only re-download if the file has changed (useget_headers()to fetch remote headers). - Large Files: If downloading very large files, replace
file_get_contentswith a stream-based approach (e.g., usingfopenandfwritein chunks) to avoid memory issues.
内容的提问来源于stack exchange,提问作者tagueule
相关产品推荐
相关产品推荐

