absPath($key); $dir = dirname($target); if (!is_dir($dir) && !@mkdir($dir, 0755, true)) { return ['ok' => false, 'error' => "无法创建目录:{$dir}"]; } if (@rename($srcPath, $target)) { @chmod($target, 0644); return ['ok' => true, 'error' => '']; } if (@copy($srcPath, $target)) { @unlink($srcPath); @chmod($target, 0644); return ['ok' => true, 'error' => '']; } return ['ok' => false, 'error' => "写入文件失败:{$key}(请检查目录权限)"]; } public function putContents($binary, $key, $mime = null) { $target = $this->absPath($key); $dir = dirname($target); if (!is_dir($dir) && !@mkdir($dir, 0755, true)) { return ['ok' => false, 'error' => "无法创建目录:{$dir}"]; } if (@file_put_contents($target, $binary) === false) { return ['ok' => false, 'error' => "写入文件失败:{$key}(请检查目录权限)"]; } @chmod($target, 0644); return ['ok' => true, 'error' => '']; } public function delete($key) { $target = $this->absPath($key); if (is_file($target)) { return @unlink($target); } return true; // 不存在视为成功 } public function url($key) { return '/' . ltrim((string)$key, '/'); } public function exists($key) { return is_file($this->absPath($key)); } /** * 服务端读取对象。本地驱动直接读盘。 * 返回结构与 storageHttp() 同构(etag / last-modified 供 media.php 做 304 判断)。 */ public function get($key, array $extra = []) { $abs = $this->absPath($key); if (!is_file($abs) || !is_readable($abs)) { return ['ok' => false, 'status' => 404, 'headers' => [], 'body' => '', 'error' => '对象不存在']; } $body = @file_get_contents($abs); if ($body === false) { return ['ok' => false, 'status' => 500, 'headers' => [], 'body' => '', 'error' => '读取文件失败']; } return [ 'ok' => true, 'status' => 200, 'headers' => [ 'content-type' => storageMimeOfFile($abs), 'content-length' => (string)strlen($body), 'etag' => '"' . md5($body) . '"', 'last-modified' => gmdate('D, d M Y H:i:s \G\M\T', (int)@filemtime($abs)), ], 'body' => $body, 'error' => '', ]; } public function test() { // 验证上传目录可写 $probe = UPLOAD_DIR . '.storage_test_' . uniqid(); if (@file_put_contents($probe, 'ok') === false) { return ['ok' => false, 'error' => '上传目录不可写:' . UPLOAD_DIR]; } @unlink($probe); $free = @disk_free_space(ROOT_DIR); $msg = '本地存储正常,上传目录可写。'; if ($free !== false) { $msg .= ' 剩余空间 ' . formatBytes($free) . '。'; } return ['ok' => true, 'error' => '', 'message' => $msg]; } private function absPath($key) { return ROOT_DIR . '/' . ltrim((string)$key, '/'); } } // 字节数格式化(设置页展示用) function formatBytes($bytes, $precision = 2) { $units = ['B', 'KB', 'MB', 'GB', 'TB']; $bytes = max(0, (float)$bytes); $i = 0; while ($bytes >= 1024 && $i < count($units) - 1) { $bytes /= 1024; $i++; } return round($bytes, $i === 0 ? 0 : $precision) . ' ' . $units[$i]; }