'qiniu', 'qiniu' => [ 'root' => 'business-files', 'private_domain' => 'https://caiwu.jiansun.vip', 'access_key' => 'test-access-key', 'secret_key' => 'test-secret-key', 'signed_url_ttl' => 300, ], ]; $expiration = (new DateTimeImmutable())->setTimestamp(1_800_000_000); $signer = new QiniuPrivateUrlSigner($configuration); $url = $signer->forPath('2026/07/27/1/发票 图片.png', $expiration); $urlToSign = 'https://caiwu.jiansun.vip/business-files/2026/07/27/1/' . '%E5%8F%91%E7%A5%A8%20%E5%9B%BE%E7%89%87.png?e=1800000000'; $signature = strtr( base64_encode(hash_hmac('sha1', $urlToSign, 'test-secret-key', true)), '+/', '-_' ); $this->assertSame( strstr($urlToSign, '?', true), $signer->unsignedUrlForPath('2026/07/27/1/发票 图片.png') ); $this->assertSame( $urlToSign . '&token=test-access-key:' . $signature, $url ); } public function test_it_rejects_incomplete_private_storage_credentials(): void { $signer = new QiniuPrivateUrlSigner([ 'driver' => 'qiniu', 'qiniu' => [ 'root' => 'business-files', 'private_domain' => 'https://caiwu.jiansun.vip', 'access_key' => '', 'secret_key' => '', ], ]); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('业务文件存储凭据不完整'); $signer->forPath('document.pdf'); } public function test_it_rejects_parent_directory_segments(): void { $signer = new QiniuPrivateUrlSigner([ 'driver' => 'qiniu', 'qiniu' => [ 'root' => 'business-files', 'private_domain' => 'https://caiwu.jiansun.vip', 'access_key' => 'test-access-key', 'secret_key' => 'test-secret-key', ], ]); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('业务文件路径无效'); $signer->forPath('../document.pdf'); } }