v13.37.0 实现的确认。基本操作请参阅文件存储,服务提供者的加载请参阅包自动发现。
区分驱动名与磁盘名
Storage::extend() 的第 1 个参数是驱动名。而 Storage::disk() 的参数是在 filesystems.disks 中定义的磁盘名。可以定义多个使用同一驱动的磁盘。
FilesystemManager::resolve() 会查找与配置中 driver 对应的自定义创建处理,如果存在,则优先于内置驱动调用它。如果把 local 或 s3 用作扩展名,就会替换已有的创建处理,因此请选择包专属的名称。
在服务提供者中只注册创建方式
下面是为了理解注册路径而复用 Laravel 标准本地驱动创建处理的最小示例。如果只是为了本地存储,使用普通的local 就足够了。如果要提供自定义后端,请像官方文档的示例那样构建 Flysystem 适配器,并替换为用 Laravel 的 FilesystemAdapter 包装的处理。
runningInConsole() 限制注册。另外,只负责注册扩展的服务提供者不要设为延迟服务提供者。
回调会接收应用实例以及待解析磁盘的完整配置。配置验证和客户端创建应在回调内进行,boot() 中不要执行文件操作。示例中的 createLocalDriver() 在创建时可能会创建目录,因此区分注册时与创建时非常重要。
使用者在 config/filesystems.php 的 disks 数组中添加配置。
本例只展示本地存储的创建路径。通过
serve 提供文件或提供临时 URL 不在讨论范围内。即使在自定义适配器中支持 URL 生成、可见性、只读等功能,也请分别确认各自的实现与所需的依赖包,不要认为只要传入配置键就会生效。命名磁盘在创建后会被保留
disk('reports') 并不会每次都调用回调。Manager 的 get() 会优先使用内部 $disks 数组中已创建的实例。只有在尚未创建时才会经过 resolve(),创建结果以磁盘名为键保留下来。
因此,即使在同一进程中执行以下操作,已创建的磁盘也不会自动更新。
- 通过
config()修改filesystems.disks.reports。 - 对同一驱动名再次调用
Storage::extend()。
FilesystemServiceProvider 也会将 Manager 本身注册为 singleton。在长时间运行的进程中,不要假定每个请求或任务都一定会重新创建磁盘。
配置变更后只重新解析需要的磁盘
在同一进程中修改配置并进行验证时,要把配置更新与移除保留的磁盘分开处理。forgetDisk() 会从 Manager 的保留数组中移除指定名称。下一次调用 disk() 时会重新读取当前配置。在所确认的版本中,purge() 的实现同样是移除指定名称,并不会调用自定义后端的连接关闭方法。
在生产环境修改配置或更新包时,请按照常规部署流程重新生成配置缓存,并重启长时间运行的 worker。配置缓存与本进程内的磁盘保留数组是两回事。
临时配置使用 build
如果不想修改命名的共享配置,可以使用按需磁盘。build() 会使用指定配置直接调用 resolve(),不会将结果加入命名磁盘的保留数组。虽然需要注册自定义驱动,但每次调用都会进入创建处理。即使使用动态存储位置,也不要设计成可以根据用户输入无限制地选择存储位置或连接目标。
在 fake 之外单独测试创建路径
Storage::fake('reports') 会通过 Manager 的 set() 将该磁盘替换为测试用的本地磁盘。它对应用的文件操作测试很方便,但并不能验证包的自定义创建处理是否被正确注册和执行。
在包的测试环境中加载 ArchiveServiceProvider 后,分别验证以下内容。
对于自定义外部后端,要把不使用网络的创建测试与在明确准备的验证环境中进行的连接测试分开。不要把 fake 的成功当作与真实服务兼容的证据。
长期维护检查清单
- 将驱动名、配置键、返回值的 API、异常及
throw的处理作为公开规范写入文档。 - 使用不会与标准驱动或其他包冲突的扩展名。
- 不要仅在服务提供者启动时就解析或使用磁盘。
- 修改配置时,区分配置缓存、Manager 的保留数组、使用方的引用以及其他进程。
- 针对 Laravel 的每个支持版本,持续验证创建、重新解析以及实际的文件操作。