You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

388 lines
10 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Filesystem;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use League\Flysystem\Adapter\AbstractAdapter;
use League\Flysystem\Config;
class HttpGoAdapter extends AbstractAdapter
{
protected Client $client;
protected string $baseUri;
public function __construct(string $baseUri)
{
$this->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,
];
}
}