configService()->isOss() ? $this->configService()->objectKey($relativePath) : $relativePath; return $this->resolveBaseUrl($request) . '/' . ltrim($publicPath, '/'); } public function normalizeUrl(string $value, Request $request): string { $value = trim($value); if ($value === '') { return ''; } $parts = parse_url($value); $path = (string)($parts['path'] ?? ''); $query = (string)($parts['query'] ?? ''); $fragment = (string)($parts['fragment'] ?? ''); $host = strtolower((string)($parts['host'] ?? '')); $scheme = (string)($parts['scheme'] ?? ''); if ($scheme === '' && $host === '') { return $this->appendQueryAndFragment( $this->buildUrl($request, $path !== '' ? $path : $value), $query, $fragment ); } if ($path !== '' && $this->shouldRewriteHost($host)) { return $this->appendQueryAndFragment( $this->buildUrl($request, $path), $query, $fragment ); } return $value; } public function storagePath(string $value): string { $value = trim($value); if ($value === '') { return ''; } $path = parse_url($value, PHP_URL_PATH); if (!is_string($path) || $path === '') { $path = ltrim($value, '/'); return $this->configService()->isOss() ? $this->configService()->removePathPrefix($path) : $path; } $path = ltrim($path, '/'); return $this->configService()->isOss() ? $this->configService()->removePathPrefix($path) : $path; } private function resolveBaseUrl(Request $request): string { $configured = $this->configService()->publicBaseUrl(); if ($configured !== '') { return $configured; } $configured = trim((string)($_ENV['PUBLIC_FILE_BASE_URL'] ?? ($_ENV['APP_PUBLIC_BASE_URL'] ?? ''))); if ($configured !== '') { return $this->normalizeBaseUrl($configured); } $scheme = trim((string)($request->header('x-forwarded-proto') ?: 'http')); $host = trim((string)($request->header('x-forwarded-host') ?: $request->header('host') ?: $request->host())); if (($commaPos = strpos($host, ',')) !== false) { $host = trim(substr($host, 0, $commaPos)); } $port = trim((string)($request->header('x-forwarded-port') ?: '')); if ($port !== '' && strpos($host, ':') === false) { if (!(($scheme === 'http' && $port === '80') || ($scheme === 'https' && $port === '443'))) { $host .= ':' . $port; } } return $this->normalizeBaseUrl($scheme . '://' . $host); } private function normalizeBaseUrl(string $baseUrl): string { $baseUrl = trim($baseUrl); if ($baseUrl === '') { return ''; } if (!preg_match('/^https?:\/\//i', $baseUrl)) { $baseUrl = 'https://' . ltrim($baseUrl, '/'); } return rtrim($baseUrl, '/'); } private function configService(): FileStorageConfigService { return new FileStorageConfigService(); } private function shouldRewriteHost(string $host): bool { if ($host === '') { return false; } if (in_array($host, ['localhost', '127.0.0.1', '0.0.0.0', 'host.docker.internal'], true)) { return true; } return preg_match('/^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.)/', $host) === 1; } private function appendQueryAndFragment(string $url, string $query, string $fragment): string { if ($query !== '') { $url .= '?' . $query; } if ($fragment !== '') { $url .= '#' . $fragment; } return $url; } }