使用PHP生成Google Cloud Storage签名URL时重复生成相同结果的问题求助
First, let's break down your two main issues: the identical signed URLs on repeated script runs, and the potential underlying problem with your signing logic that might have caused initial failures.
Why Your URLs Are Cached
The core issue here is server-side caching—either PHP opcode caching (like OPcache) or web server output caching. When OPcache is enabled, it can cache the result of function executions, including the DateTime object you used to calculate the expiration timestamp. This means every call to your function reuses the same fixed expiration time, resulting in identical signatures every time until you clear the cache.
Quick Fixes for Caching
Here are two key changes to ensure fresh URLs on every run:
Replace
DateTimewith a direct timestamp calculation
Ditch theDateTimeobject and usetime() + $durationinstead. This is a simpler, cache-resistant way to generate a fresh expiration timestamp on every function call.Add anti-cache headers
Tell browsers and your web server not to cache the script's output by adding these headers at the top of your file.
Corrected Working Code
<?php // Prevent caching of this script's output header("Cache-Control: no-cache, no-store, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); $filepath = 'my file.zip'; // No bucket name, no leading slash $bucket = 'mybucket'; $key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; // Your access key ID $secret = 'xxxxxxxxxxxxxxxxxxxxxxx'; // Your secret key function getSignedGoogleCloudStorageUrl($filepath, $bucket, $secret, $key, $duration = 300) { // Generate fresh expiration timestamp on every call $seconds = time() + $duration; // Properly encode the file path segments to handle spaces/special chars $objectPieces = explode('/', $filepath); array_walk($objectPieces, function (&$piece) { $piece = rawurlencode($piece); }); $objectName = implode('/', $objectPieces); $resource = sprintf('/%s/%s', $bucket, $objectName); $headers = []; // Add headers like x-goog-acl here if needed for your use case // Build the string that needs to be signed $toBeSignedArray = [ 'GET', '', // Content-MD5 (leave blank unless required) '', // Content-Type (leave blank unless required) $seconds, implode("\n", $headers) . $resource, ]; $toBeSignedString = implode("\n", $toBeSignedArray); // Choose the signing method that matches your key type: // Option 1: For HMAC Interoperable Access Keys (your original logic) $encodedSignature = urlencode(base64_encode(hash_hmac('sha1', $toBeSignedString, $secret, true))); // Option 2: For Service Account RSA Private Keys (uncomment if using a service account) // $signature = ''; // // Ensure $secret is the raw private key (remove -----BEGIN/END PRIVATE KEY----- and newlines) // openssl_sign($toBeSignedString, $signature, $secret, OPENSSL_ALGO_SHA256); // $encodedSignature = urlencode(base64_encode($signature)); // Assemble the final signed URL $query = [ 'GoogleAccessId=' . $key, 'Expires=' . $seconds, 'Signature=' . $encodedSignature, ]; return "https://storage.googleapis.com/{$bucket}/{$filepath}?" . implode('&', $query); } echo getSignedGoogleCloudStorageUrl($filepath, $bucket, $secret, $key); ?>
Additional Checks to Ensure Reliability
- OPcache Override: If you still see cached results, add
opcache_invalidate(__FILE__, true);at the top of your script to force OPcache to refresh the current file. - Web Server Caching: Double-check your Nginx/Apache config to ensure you're not caching PHP script outputs (e.g., disable
proxy_cachein Nginx ormod_cachein Apache for this specific script). - Key Type Match: A common reason initial scripts failed is using a service account RSA key with HMAC signing logic. If you're using a service account (not HMAC keys), switch to the RSA signing code in the example above.
内容的提问来源于stack exchange,提问作者bob williams

