baseUri = rtrim($baseUri, '/'); $this->client = new Client([ 'base_uri' => $this->baseUri . '/', 'timeout' => 30, 'http_errors' => false, ]); } /** * 写入文件(base64 方式) */ public function write($path, $contents, Config $config): array|false { $fullPath = $this->applyPathPrefix($path); $dir = dirname($fullPath); $filename = basename($fullPath); try { $response = $this->client->post('api/upload/base64', [ 'json' => [ 'file' => base64_encode($contents), 'path' => $dir, 'filename' => $filename, ], ]); if ($response->getStatusCode() !== 200) { return false; } $body = json_decode($response->getBody(), true); if (($body['code'] ?? 0) !== 200) { return false; } return $this->normalizeMetadata($fullPath, $config); } catch (GuzzleException $e) { return false; } } /** * 写入文件流(multipart 方式) */ public function writeStream($path, $resource, Config $config): array|false { $fullPath = $this->applyPathPrefix($path); $dir = dirname($fullPath); $filename = basename($fullPath); // 将流转换为临时文件 $tmpFile = tmpfile(); stream_copy_to_stream($resource, $tmpFile); $meta = stream_get_meta_data($tmpFile); try { $response = $this->client->post('api/upload/file', [ 'multipart' => [ [ 'name' => 'file', 'contents' => fopen($meta['uri'], 'r'), 'filename' => $filename, ], [ 'name' => 'path', 'contents' => $dir, ], ], ]); fclose($tmpFile); if ($response->getStatusCode() !== 200) { return false; } $body = json_decode($response->getBody(), true); if (($body['code'] ?? 0) !== 200) { return false; } return $this->normalizeMetadata($fullPath, $config); } catch (GuzzleException $e) { fclose($tmpFile); return false; } } /** * 更新文件(委托给 write) */ public function update($path, $contents, Config $config): array|false { return $this->write($path, $contents, $config); } /** * 更新文件流(委托给 writeStream) */ public function updateStream($path, $resource, Config $config): array|false { return $this->writeStream($path, $resource, $config); } /** * 读取文件内容 */ public function read($path): array|false { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->get('files/' . ltrim($fullPath, '/')); if ($response->getStatusCode() !== 200) { return false; } return ['contents' => $response->getBody()->getContents()]; } catch (GuzzleException $e) { return false; } } /** * 读取文件流 */ public function readStream($path): array|false { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->get('files/' . ltrim($fullPath, '/'), [ 'stream' => true, ]); if ($response->getStatusCode() !== 200) { return false; } // 返回一个临时流 $tmpFile = tmpfile(); $stream = $response->getBody()->detach(); if ($stream === null) { fclose($tmpFile); return false; } stream_copy_to_stream($stream, $tmpFile); fclose($stream); rewind($tmpFile); return ['stream' => $tmpFile]; } catch (GuzzleException $e) { return false; } } /** * 检查文件是否存在 */ public function has($path): bool { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->head('files/' . ltrim($fullPath, '/')); return $response->getStatusCode() === 200; } catch (GuzzleException $e) { return false; } } /** * 删除文件 */ public function delete($path): bool { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->delete('files/' . ltrim($fullPath, '/')); return $response->getStatusCode() === 200; } catch (GuzzleException $e) { return false; } } /** * 删除目录 */ public function deleteDir($dirname): bool { $fullPath = $this->applyPathPrefix($dirname); try { $response = $this->client->delete('files/' . ltrim($fullPath, '/')); return $response->getStatusCode() === 200; } catch (GuzzleException $e) { return false; } } /** * 创建目录(Go 服务隐式创建,直接返回成功) */ public function createDir($dirname, Config $config): array|false { return ['path' => $dirname, 'type' => 'dir']; } /** * 设置可见性(始终 public) */ public function setVisibility($path, $visibility): array|false { return ['visibility' => $visibility, 'path' => $path]; } /** * 获取可见性 */ public function getVisibility($path): array|false { return ['visibility' => 'public', 'path' => $path]; } /** * 获取 MIME 类型 */ public function getMimetype($path): array|false { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->head('files/' . ltrim($fullPath, '/')); if ($response->getStatusCode() !== 200) { return false; } $contentType = $response->getHeader('Content-Type')[0] ?? 'application/octet-stream'; return ['mimetype' => $contentType]; } catch (GuzzleException $e) { return false; } } /** * 获取文件大小 */ public function getSize($path): array|false { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->head('files/' . ltrim($fullPath, '/')); if ($response->getStatusCode() !== 200) { return false; } $size = (int)($response->getHeader('Content-Length')[0] ?? 0); return ['size' => $size]; } catch (GuzzleException $e) { return false; } } /** * 获取文件修改时间 */ public function getTimestamp($path): array|false { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->head('files/' . ltrim($fullPath, '/')); if ($response->getStatusCode() !== 200) { return false; } $lastModified = $response->getHeader('Last-Modified')[0] ?? ''; $timestamp = $lastModified ? strtotime($lastModified) : time(); return ['timestamp' => $timestamp]; } catch (GuzzleException $e) { return false; } } /** * 获取文件元信息 */ public function getMetadata($path): array|false { $fullPath = $this->applyPathPrefix($path); try { $response = $this->client->head('files/' . ltrim($fullPath, '/')); if ($response->getStatusCode() !== 200) { return false; } $contentType = $response->getHeader('Content-Type')[0] ?? 'application/octet-stream'; $size = (int)($response->getHeader('Content-Length')[0] ?? 0); $lastModified = $response->getHeader('Last-Modified')[0] ?? ''; $timestamp = $lastModified ? strtotime($lastModified) : time(); return [ 'type' => 'file', 'path' => $fullPath, 'timestamp' => $timestamp, 'size' => $size, 'mimetype' => $contentType, ]; } catch (GuzzleException $e) { return false; } } /** * 列出目录内容(简化实现,返回空数组) */ public function listContents($directory = '', $recursive = false): array { return []; } /** * 重命名文件 */ public function rename($path, $newpath): bool { $contents = $this->read($path); if ($contents === false) { return false; } if ($this->write($newpath, $contents['contents'], new Config()) === false) { return false; } return $this->delete($path); } /** * 复制文件 */ public function copy($path, $newpath): bool { $contents = $this->read($path); if ($contents === false) { return false; } return $this->write($newpath, $contents['contents'], new Config()) !== false; } /** * 标准化元数据 */ protected function normalizeMetadata(string $path, Config $config): array { return [ 'type' => 'file', 'path' => $path, 'timestamp' => time(), 'size' => 0, ]; } }