直接装 league/flysystem 核心包即可,按需添加适配器;正确初始化 Adapter 并传入 Filesystem 构造函数;跨存储用 MountManager;异常捕获应限定为 FilesystemException。

直接装 core 包就能用,别一上来就全装
Flysystem 的核心功能都在 league/flysystem 这个包里,它不包含任何具体存储适配器(比如 S3、FTP、本地磁盘),只提供统一的 Filesystem 类和基础接口。很多新手看到文档里一堆 composer require league/flysystem-aws-s3-v3 就跟着全装一遍,结果引发依赖冲突——比如 aws/aws-sdk-php 版本撞上 Flysystem 声明的 "3.209.31 || 3.210.0" 冲突项,composer install 直接报错。
实操建议:
- 先只运行
composer require league/flysystem,验证基础读写是否通 - 等确定要用哪种存储,再单独加对应适配器,例如:需要 FTP 就补
composer require league/flysystem-ftp,要阿里云 OSS 就加composer require "iidestiny/flysystem-oss:^4" - 遇到冲突时,用
composer why aws/aws-sdk-php查谁在拉旧版,比硬删vendor理性得多
Adapter 和 Filesystem 是两层,漏掉任一都跑不起来
常见错误现象是写了 $filesystem->write(...) 却抛出 TypeError: Argument 1 passed to League\Flysystem\Filesystem::__construct() must be an instance of League\Flysystem\AdapterInterface ——本质是只 new 了适配器,没传给 Filesystem 构造函数,或者用了过时写法(v1 和 v2/v3 的 Adapter 初始化方式不同)。
以本地存储为例,v3 正确写法是:
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
$adapter = new LocalFilesystemAdapter('/var/www/uploads');
$filesystem = new Filesystem($adapter); // 必须这一步!
$filesystem->write('test.txt', 'ok');
注意:LocalFilesystemAdapter 不是 LocalAdapter(那是 v1),也不是 Adapter(不存在)。路径参数必须是绝对路径,相对路径如 ./uploads 在 CLI 下可能工作,但 Web 环境下因 getcwd() 不稳定极易出错。
跨存储同步得靠 MountManager,不是靠 copy 方法硬搬
有人以为只要两个 Filesystem 实例都有 copy() 方法,就能从本地拷到 S3,结果发现 $localFs->copy('a.txt', 's3://b.txt') 报错:方法只支持同实例内路径。真正支持跨源操作的是 MountManager,它才是 Flysystem 设计的“多端调度中枢”。
实操要点:
-
MountManager构造时传的是[key => adapter]数组,key 名会成为协议前缀,比如'local' => $localAdapter,后续路径就得写local://file.txt - 复制动作是
$mountManager->copy('local://a.txt', 's3://a.txt'),不是$s3Fs->copy(...) - 如果某端适配器初始化失败(比如 S3 凭据不对),
MountManager不会在构造时报错,而是在第一次调用copy时才抛FilesystemException,调试时容易误判位置
异常捕获必须限定为 FilesystemException,别用 Exception 大包抄
文件操作失败时,Flysystem 统一抛 League\Flysystem\FilesystemException 及其子类(如 UnableToWriteFile、UnableToReadFile)。如果写成 catch (\Exception $e),会把 PHP 原生错误(比如内存不足、超时)也吞掉,掩盖真实问题;更糟的是,某些适配器(如 FTP)在连接中断后会反复重试再抛异常,大包抄会让日志里塞满重复堆栈。
正确姿势:
try {
$filesystem->write('data.json', json_encode($data));
} catch (\League\Flysystem\FilesystemException $e) {
error_log('Flysystem write failed: ' . $e->getMessage());
// 这里可区分处理:记录告警、触发降级(如切本地缓存)、返回用户友好提示
}
另外,fileExists()、listContents() 这些方法本身不抛异常,但返回 false 或空数组时,往往意味着底层已出问题(比如 FTP 被防火墙拦截),不能只当“文件不存在”处理。
最常被跳过的其实是适配器的连接健康检查逻辑——比如 FTP 的 passive 模式在某些 NAT 环境下必挂,但错误只出现在第一次 listContents() 之后,之前一切看起来都正常。别等上线才发现上传卡住三分钟才报错。











