文件存储

文件存储

简介

Laravel 提供了一个强大的文件系统抽象,这得益于 Frank de Jonge 强大的 Flysystem 扩展包。Laravel 文件系统集成为使用本地文件系统、Amazon S3 和 Rackspace 云存储提供了简单易用的驱动程序。更棒的是,由于每个系统的 API 保持不变,所以在这些存储选项之间切换是非常简单的。

配置

文件系统的配置文件位于 config/filesystems.php 。在这个文件中你可以配置所有「磁盘」。每个磁盘代表特定的存储驱动及存储位置。每种支持的驱动程序的示例配置都包含在配置文件中。因此,只需要修改配置即可反映你的存储偏好和凭据。

当然,你可以根据需要配置多个磁盘,甚至你还可以使多个磁盘共用同一个驱动。

公共磁盘

public 磁盘适用于要公开访问的文件。默认情况下, public 磁盘使用 local 驱动,并且将这些文件存储在 storage/app/public 目录下。为了使它们能通过网络访问,你需要创建 public/storagestorage/app/public 的符号链接。这种方式能把可公开访问文件都保留在同一个目录下,以便在使用零停机时间部署系统如 Envoyer 的时候,就可以轻松地在不同的部署之间共享这些文件。你可以使用 Artisan 命令 storage:link 来创建符号链接:

你可以使用 Artisan 命令 storage:link 来创建符号链接:

php artisan storage:link

当然,一旦一个文件被存储并且已经创建了符号链接,你就可以使用辅助函数 asset 来创建文件的 URL:

echo asset('storage/file.txt');

本地驱动

使用 local 驱动时,所有文件操作都与你在配置文件中定义的 root 目录相关。该目录的默认值是 storage/app 。因此,以下方法会把文件存储在 storage/app/file.txt 中:

Storage::disk('local')->put('file.txt', 'Contents');

驱动程序先决条件

Composer 包

在使用 SFTP、S3 或 Rackspace 等驱动之前,你需要通过 Composer 安装相应的软件包:

  • SFTP: league/flysystem-sftp ~1.0
  • Amazon S3: league/flysystem-aws-s3-v3 ~1.0
  • Rackspace: league/flysystem-rackspace ~1.0

使用缓存适配器是提高性能的一个绝对必要条件。你需要一个额外的包:

  • CachedAdapter: league/flysystem-cached-adapter ~1.0

S3 驱动配置

S3 驱动配置信息位于你的 config/filesystems.php 配置文件中。该文件包含 S3 驱动程序的示例配置数组。 你可以自由使用你自己的 S3配置和凭证修改此阵列。 为方便起见,这些环境变量与 AWS CLI 使用的命名约定相匹配。

FTP 驱动配置

Laravel 的文件系统集成能很好的支持 FTP,不过 FTP 的配置示例并没有被包含在框架默认的 filesystems.php 文件中。需要的话可以使用下面的示例配置:

'ftp' => [
    'driver'   => 'ftp',
    'host'     => 'ftp.example.com',
    'username' => 'your-username',
    'password' => 'your-password',

    // 可选的 FTP 配置项...
    // 'port'     => 21,
    // 'root'     => '',
    // 'passive'  => true,
    // 'ssl'      => true,
    // 'timeout'  => 30,
],

SFTP 驱动器的配置

Laravel 的 Flysystem 集成包与 SFTP 协同得非常好;不过,在该框架的默认配置文件 filesystems.php 中并没有包含示范配置。如果要配置 SFTP 文件系统,可以使用如下示例配置:

'sftp' => [
    'driver' => 'sftp',
    'host' => 'example.com',
    'username' => 'your-username',
    'password' => 'your-password',

    // 基于 SSH 密钥的身份验证设置...
    // 'privateKey' => '/path/to/privateKey',
    // 'password' => 'encryption-password',

    // 可选的 SFTP 配置...
    // 'port' => 22,
    // 'root' => '',
    // 'timeout' => 30,
],

Rackspace 驱动器配置

Laravel 的 Flysystem 集成包与 Rackspace 协同得非常好;不过,在该框架的默认配置文件 filesystems.php 中并没有包含示范配置。如果要配置 Rackspace 文件系统,可以使用如下示例配置:

'rackspace' => [
    'driver'    => 'rackspace',
    'username'  => 'your-username',
    'key'       => 'your-key',
    'container' => 'your-container',
    'endpoint'  => 'https://identity.api.rackspacecloud.com/v2.0/',
    'region'    => 'IAD',
    'url_type'  => 'publicURL',
],

缓存

给指定磁盘开启缓存功能,需要在该磁盘的配置项中直接添加 cachecache 选项应该是一个缓存配置的数组,由缓存驱动名称 store (译者注:文档原始描述文字 disk 与示例代码中的 store 不一致,验证代码后的确应该是 store ,故作此修改。)、 单位为秒的过期时间 expire ,以及缓存前缀 prefix 组成:

's3' => [
    'driver' => 's3',

    // 驱动器其他配置...

    'cache' => [
        'store' => 'memcached',
        'expire' => 600,
        'prefix' => 'cache-prefix',
    ],
],

获取磁盘实例

Storage 门面可用于与任何已配置的磁盘进行交互。例如,你可以使用门面中的 put 方法将头像存储到默认磁盘。如果你使用 Storage 门面中的任何方法,而一开始并没有使用 disk 方法,那么所调用的方法会自动传递给默认的磁盘:

use Illuminate\Support\Facades\Storage;

Storage::put('avatars/1', $fileContents);

如果应用程序要与多个磁盘进行互操作,可使用 Storage 门面中的 disk 方法对特定磁盘上的文件进行操作:

Storage::disk('s3')->put('avatars/1', $fileContents);

检索文件

get 方法可以用于检索文件的内容,此方法返回该文件的原始字符串内容。 切记,所有文件路径的指定都应该相对于为磁盘配置的「root」目录:

$contents = Storage::get('file.jpg');

exists 方法可以用来判断磁盘上是否存在指定的文件:

$exists = Storage::disk('s3')->exists('file.jpg');

下载文件

download 方法可用于生成一个响应,强制用户的浏览器在给定路径下载文件。 download 方法接受一个文件名作为该方法的第二个参数,它将确定用户下载文件时看到的文件名。最后,你可以传递一个 HTTP 数组头作为该方法的第三个参数:

return response()->download('file.jpg');

return response()->download('file.jpg', $name, $headers);

文件 URLs

你可以使用 url 方法来获取给定文件的 URL。如果你使用的是 local 驱动,一般只是在给定的路径上加上 /storage 并返回一个相对的 URL 到那个文件。如果使用的是 s3 或者是 rackspace 驱动,会返回完整的远程 URL:

use Illuminate\Support\Facades\Storage;
$url = Storage::url('file.jpg');

注意:切记,如果使用的是 local 驱动,则所有想被公开访问的文件都应该放在 storage/app/public 目录下。此外你应该在 public/storage 创建一个符号链接 来指向 storage/app/public 目录。

临时 URLs

当使用 s3rackspace 驱动来存储文件,可以使用 temporaryUrl 方法创建给定文件的临时 URL。这个方法会接收路径和 DateTime 实例来指定 URL 何时过期:

$url = Storage::temporaryUrl(
    'file.jpg', now()->addMinutes(5)
);

自定义本地 URL 主机

如果要使用 local 驱动为存储在磁盘上的文件预定义主机,可以向磁盘配置数组添加一个 url 选项:

'public' => [
    'driver' => 'local',
    'root' => storage_path('app/public'),
    'url' => env('APP_URL').'/storage',
    'visibility' => 'public',
],

文件元数据

除了读写文件外,Laravel 还可以提供有关文件本身的信息,例如,size 方法可用来获取文件的大小(以字节为单位):

use Illuminate\Support\Facades\Storage;

$size = Storage::size('file.jpg');

lastModified 方法返回文件最后一次被修改的 UNIX 时间戳:

$time = Storage::lastModified('file.jpg');

保存文件

put 方法可用于将原始文件内容保存到磁盘上。你也可以传递 PHP 的 resourceput 方法,它将使用文件系统下的底层流支持。强烈建议在处理大文件时使用此方法:

use Illuminate\Support\Facades\Storage;

Storage::put('file.jpg', $contents);

Storage::put('file.jpg', $resource);

自动流式传输

如果你想 Laravel 自动将给定文件流式传输到你的存储位置,你可以使用 putFileputFileAs 方法。这个方法接收 Illuminate\Http\FileIlluminate\Http\UploadedFile 实例,并自动将文件流式传输到你想要传输的位置:

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;

// 自动为文件名生成唯一的ID...
Storage::putFile('photos', new File('/path/to/photo'));

// 手动指定文件名...
Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

关于 putFile 方法,有些点需要注意。我们指定的是一个目录名而不是文件名。默认情况下, putFile 方法会生成一个唯一的 ID 作为文件名。文件的扩展名根据检测文件的 MIME 类型来确定。 putFile 方法会返回文件路径,以便你可以将文件路径(包括生成的文件名)存储在数据库中。

putFileputFileAs 方法也接受一个方法来指定存储文件的 「可见性」。如果你将文件存储在诸如 S3 的云盘上,并且想让该文件公开访问,则可以使用以下功能:

Storage::putFile('photos', new File('/path/to/photo'), 'public');

文件数据写入

prependappend 方法允许你在文件的开头或结尾写入数据:

Storage::prepend('file.log', 'Prepended Text');

Storage::append('file.log', 'Appended Text');

复制 & 移动文件

copy 方法用来复制文件到磁盘上的新位置,而 move 方法用来重命名或移动文件到新位置:

Storage::copy('old/file.jpg', 'new/file.jpg');

Storage::move('old/file.jpg', 'new/file.jpg');

文件上传

在 web 应用程序中,最常用到的文件存储的场景的地方就是上传头像,照片和文件。Laravel 上传文件的实例方法 store 可以轻松的处理文件上传存储问题。你只需要调用带有文件保存路径作为参数的 store 方法即可:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserAvatarController extends Controller
{
    /**
     * 更新用户头像.
     *
     * @param  Request  $request
     * @return Response
     */
    public function update(Request $request)
    {
        $path = $request->file('avatar')->store('avatars');

        return $path;
    }
}

上例有几个点需要注意。我们指定的是目录名,而不是文件名。默认情况下, store 方法会自动生成唯一的ID作为文件名。文件的扩展名将通过检查文件的 MIME 类型来确定。该文件的路径和文件名会被 store 方法返回,以便后续数据库的存储使用。

你也可以使用 Storage facade 上的 putFile 方法达到和上例同样的效果:

$path = Storage::putFile('avatars', $request->file('avatar'));

指定文件名

如果你不想将文件名自动分配给存储的文件,可以使用 storeAs 方法,该方法接受路径,文件名和 (可选) 磁盘作为其参数:

$path = $request->file('avatar')->storeAs(
    'avatars', $request->user()->id
);

你可以使用 Storage facade 上的 putFileAs 方法达到和上例同样的文件操作:

$path = Storage::putFileAs(
    'avatars', $request->file('avatar'), $request->user()->id
);

指定磁盘

默认情况下, store 方法使用默认磁盘。如果你需要指定其他的磁盘,可以传入磁盘名作为 store 方法的第二个参数:

$path = $request->file('avatar')->store(
    'avatars/'.$request->user()->id, 's3'
);

文件可见性

在 Laravel 集成的文件系统中,「可见性」是对多个平台的文件权限的抽象。文件可以声明为 publicprivate。如果一个文件被声明为 public,意味着其他人可以访问。例如,使用 S3 驱动时,就可以检索声明为 public 的文件。

你可以通过 put 方法设置文件的可见性:

use Illuminate\Support\Facades\Storage;

Storage::put('file.jpg', $contents, 'public');

getVisibilitysetVisibility 方法可以对现存文件的可见性进行查询和设置:

$visibility = Storage::getVisibility('file.jpg');

Storage::setVisibility('file.jpg', 'public')

删除文件

delete 方法接收一个文件名或数组形式的文件名来删除磁盘上的文件:

use Illuminate\Support\Facades\Storage;

Storage::delete('file.jpg');

Storage::delete(['file.jpg', 'file2.jpg']);

必要的话,可以指定磁盘名来删除其下的文件:

use Illuminate\Support\Facades\Storage;

Storage::disk('s3')->delete('folder_path/file_name.jpg');

目录

获取目录下的所有的文件

files 方法返回指定目录下的所有文件。如果你想检索指定目录(包括子目录)中所有的文件列表,可以使用 allFiles 方法:

use Illuminate\Support\Facades\Storage;

$files = Storage::files($directory);

$files = Storage::allFiles($directory);

获取目录下的所有目录

directories 方法返回指定目录下所有目录的数组。此外,你可以使用 allDirectories 方法获取指定目录以及其子目录下所有目录的列表:

$directories = Storage::directories($directory);

// 递归...
$directories = Storage::allDirectories($directory);

创建目录

makeDirectory 方法会递归创建目录:

Storage::makeDirectory($directory);

删除目录

deleteDirectory 方法会删除指定目录及其下所有的文件:

Storage::deleteDirectory($directory);

自定义文件系统

虽然 Laravel 的文件系统提供了一些开箱即用的驱动,但是它不仅限于这些,还提供了其他文件系统的适配器。通过这些适配器,可以在 Lavarel 应用中创建自定义驱动。

为了设置自定义文件系统,你需要一个Flysystem 适配器。现在让我们把社区维护的 Dropbox 适配器添加到项目中:

composer require spatie/flysystem-dropbox

接下来,你需要创建一个名为 DropboxServiceProvider服务提供者 。在它的 boot 方法中,使用 Storage facade 的 extend 方法自定义驱动:

<?php

namespace App\Providers;

use Storage;
use League\Flysystem\Filesystem;
use Illuminate\Support\ServiceProvider;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;

class DropboxServiceProvider extends ServiceProvider
{
    /**
     * 执行注册后引导驱动.
     *
     * @return void
     */
    public function boot()
    {
        Storage::extend('dropbox', function ($app, $config) {
            $client = new DropboxClient(
                $config['authorization_token']
            );

            return new Filesystem(new DropboxAdapter($client));
        });
    }

    /**
     * 在容器中注册绑定.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

extend 方法的第一个参数是驱动的名称,第二个参数是闭包,接受 $app$config 变量。这个闭包必须返回 League\Flysystem\Filesystem 的实例。 $config 变量包含了磁盘在 config/filesystems.php 中的位置。

接下来,在config/app.php 配置文件中注册服务提供者:

'providers' => [
    // ...
    App\Providers\DropboxServiceProvider::class,
];

当你创建并注册好服务提供者后,就可以通过在 config/filesystems.php 配置文件中添加 dropbox 驱动并使用它了。

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:7
讨论数量: 9
发起讨论 只看当前版本


xiaohaia
图片上传到 storage,页面显示
0 个点赞 | 6 个回复 | 分享 | 课程版本 5.7
zhedream_
Storage::url 问题 ?
0 个点赞 | 2 个回复 | 问答 | 课程版本 5.6
akon
docker 部署 Laravel,无法访问 storage 目录中的 文件
0 个点赞 | 1 个回复 | 问答 | 课程版本 5.8
surest
使用 PHP artisan storage:link 的时候? symlink (): Input/output error?
0 个点赞 | 1 个回复 | 问答 | 课程版本 5.5
WilliamQian
copy 可以执行,rename 就报拒绝访问
0 个点赞 | 0 个回复 | 问答 | 课程版本 5.8
1164880236
Laravel5.4 没有 Storage::download 这个文件下载方法?
0 个点赞 | 0 个回复 | 问答 | 课程版本 5.4
iVerywang
Linux 命令创建软链接
0 个点赞 | 0 个回复 | 问答 | 课程版本 5.5
YvanZhu
上传文件的编码问题?
0 个点赞 | 0 个回复 | 问答 | 课程版本 5.5