logo

文件字段

Nova 提供了几种类型的文件字段:FileImageAvatarVaporFileVaporImageFile 字段是最基本的文件上传字段,也是 ImageAvatar 字段的基类。在以下文档中,我们将探讨这些字段的每一个,并讨论它们之间的异同。

概述

为了说明 Nova 文件上传字段的行为,让我们假设我们的应用程序用户可以将“个人资料照片”上传到他们的帐户。因此,我们的 users 数据库表将有一个 profile_photo 列。此列将包含磁盘上个人资料照片的路径,或者,当使用云存储提供商(如 Amazon S3)时,将包含个人资料照片在其“存储桶”中的路径。

定义字段

接下来,让我们将文件字段附加到我们的 User 资源。在本例中,我们将创建该字段并指示它将底层文件存储在 public 磁盘上。此磁盘名称应与您的应用程序 filesystems 配置文件中的磁盘名称相对应。

php
use Laravel\Nova\Fields\File;

File::make('Profile Photo')->disk('public'),

禁用文件下载

默认情况下,File 字段允许用户下载相应的文件。要禁用此功能,您可以在字段定义上调用 disableDownload 方法。

php
File::make('Profile Photo')->disableDownload(),

文件存储方式

当使用此字段上传文件时,Nova 将使用 Laravel 的 Flysystem 集成 将文件存储在您选择的磁盘上,并且该文件将被分配一个随机生成的名称。文件存储后,Nova 将将文件的相对路径存储在文件字段的底层数据库列中。

为了说明 File 字段的默认行为,让我们看一下等效的 Laravel 路由,它将以相同的方式存储文件。

php
use Illuminate\Http\Request;

Route::post('/photo', function (Request $request) {
    $path = $request->profile_photo->store('/', 'public');

    $request->user()->update([
        'profile_photo' => $path,
    ]);
});

当然,文件存储后,您可以在应用程序中使用 Laravel Storage 门面检索它。

php
use Illuminate\Support\Facades\Storage;

Storage::get($user->profile_photo);
Storage::url($user->profile_photo);

自定义

上面的文档只演示了 File 字段的默认行为。要了解有关如何自定义其行为的更多信息,请查看 自定义文档

本地磁盘

如果您正在将public磁盘与local驱动程序一起使用,则应运行php artisan storage:link Artisan 命令以从public/storage创建指向storage/app/public的符号链接。要详细了解 Laravel 中的文件存储,请查看Laravel 文件存储文档

图像

Image 字段的行为与File 字段完全相同;但是,Image 字段不会仅在 Nova 仪表板中显示文件路径,而是会显示底层文件的缩略图预览。Image 字段的所有配置和自定义选项都与File 字段相同。

php
use Laravel\Nova\Fields\Image;

Image::make('Profile Photo')->disk('public'),

要设置Image 字段在显示时的宽度,可以使用maxWidth 方法。

php
Image::make('Profile Photo')->maxWidth(100),

或者,可以使用indexWidthdetailWidth 方法为索引视图和详细信息视图设置不同的宽度。

php
Image::make('Profile Photo')->indexWidth(60)->detailWidth(150),

最大宽度

您也可以在AvatarGravatar 字段上使用maxWidthindexWidthdetailWidth 方法。

头像

Avatar 字段的行为与File 字段完全相同;但是,Avatar 字段不会仅在 Nova 仪表板中显示文件路径,而是会显示底层文件的缩略图预览。Avatar 字段的所有配置和自定义选项都与File 字段相同。

php
use Laravel\Nova\Fields\Avatar;

Avatar::make('Poster')->disk('public'),

除了显示底层文件的缩略图预览之外,Avatar 字段还会自动显示在 Nova 搜索结果中。Avatar 字段不限于“用户”资源 - 您可以在 Nova 应用程序中的任何资源上附加Avatar 字段。

Avatar Example

存储元数据

除了将文件路径存储在存储系统中,您还可以指示 Nova 存储原始客户端文件名及其大小(以字节为单位)。您可以使用 `storeOriginalName` 和 `storeSize` 方法来实现这一点。这两种方法都接受您要存储文件信息的列名。

php
use Illuminate\Http\Request;
use Laravel\Nova\Fields\File;
use Laravel\Nova\Fields\Text;

/**
 * Get the fields displayed by the resource.
 *
 * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
 * @return array
 */
public function fields(NovaRequest $request)
{
    return [
        // ...

        File::make('Attachment')
                ->disk('s3')
                ->storeOriginalName('attachment_name')
                ->storeSize('attachment_size'),

        Text::make('Attachment Name')->exceptOnForms(),

        Text::make('Attachment Size')
                ->exceptOnForms()
                ->displayUsing(function ($value) {
                    return number_format($value / 1024, 2).'kb';
                }),
    ];
}

存储原始客户端文件名的一个好处是能够使用用于上传文件的原始文件名创建文件下载响应。例如,您可以在应用程序的某个路由中执行以下操作:

php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

Route::get('/download', function (Request $request) {
    $user = $request->user();

    return Storage::download(
        $user->attachment, $user->attachment_name
    );
});

文件下载

使用 `storeOriginalName` 方法时,Nova 仪表板中文件字段的“下载”链接将自动使用原始文件名下载文件。

修剪和删除

文件字段默认情况下是可删除的,但您可以使用 `deletable` 方法覆盖此行为。

php
File::make('Photo')->disk('public')->deletable(false),

`File` 字段以及 `Image` 和 `Avatar` 字段可以标记为 `prunable`。`prunable` 方法将指示 Nova 在从数据库中删除关联模型时从存储中删除底层文件。

php
File::make('Profile Photo')->disk('public')->prunable(),

非 Nova 删除

Nova 仅会自动修剪在 Nova 中启动的模型删除操作的文件。应用程序的其他部分可能需要实现自己的文件删除逻辑。

自定义

自定义文件存储

之前我们了解到,默认情况下,Nova 使用 `Illuminate\Http\UploadedFile` 类的 `store` 方法存储文件。但是,您可以根据应用程序的需要完全自定义此行为。

自定义名称/路径

如果您只需要自定义磁盘上存储文件的名称或路径,可以使用 `File` 字段的 `path` 和 `storeAs` 方法。

php
use Illuminate\Http\Request;

File::make('Attachment')
    ->disk('s3')
    ->path($request->user()->id.'-attachments')
    ->storeAs(function (Request $request) {
        return sha1($request->attachment->getClientOriginalName());
    }),

自定义整个存储过程

但是,如果您想完全控制字段的文件存储逻辑,可以使用 `store` 方法。`store` 方法接受一个可调用对象,该对象接收传入的 HTTP 请求和与请求关联的模型实例。

php
use Illuminate\Http\Request;

File::make('Attachment')
    ->store(function (Request $request, $model) {
        return [
            'attachment' => $request->attachment->store('/', 's3'),
            'attachment_name' => $request->attachment->getClientOriginalName(),
            'attachment_size' => $request->attachment->getSize(),
        ];
    }),

如您在上面的示例中所见,`store` 回调返回一个键值对数组。这些键值对映射到您的模型实例,然后将其保存到数据库中,允许您在存储文件后更新模型的一个或多个数据库列。

这是自定义存储过程的另一个示例。在此示例中,我们使用 `store` 方法将原始文件存储在公共存储中,使用 Laravel 的队列系统创建缩略图,最后填充资源的 `media` 关系中的值。

php
use Laravel\Nova\Http\Requests\NovaRequest;

File::make('Attachment')
    ->store(function (NovaRequest $request, $model) {
        return function () use ($model, $request) {
            $media = $model->media()->updateOrCreate([], [
                'path'=> $request->file('attachment')->store('/path', 'public')
            ]);

            OptimizeMedia::dispatch($media);
        };
    }),

可调用对象

当然,在闭包中执行所有文件存储逻辑会导致您的资源变得臃肿。为此,Nova 允许您将一个“可调用”对象传递给 store 方法

php
File::make('Attachment')->store(new StoreAttachment),

可调用对象应该是一个简单的 PHP 类,只有一个 __invoke 方法

php
<?php

namespace App\Nova;

use Laravel\Nova\Http\Requests\NovaRequest;

class StoreAttachment
{
    /**
     * Store the incoming file upload.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $attribute
     * @param  string  $requestAttribute
     * @param  string|null  $disk
     * @param  string|null  $storagePath
     * @return array
     */
    public function __invoke(NovaRequest $request, $model, $attribute, $requestAttribute, $disk, $storagePath)
    {
        return [
            'attachment' => $request->attachment->store('/', 's3'),
            'attachment_name' => $request->attachment->getClientOriginalName(),
            'attachment_size' => $request->attachment->getSize(),
        ];
    }
}

自定义文件删除

当从 Nova 管理面板中删除文件时,Nova 会自动从存储中删除底层文件,并在字段关联的列中插入 NULL

如果您想覆盖此行为并提供自己的文件删除实现,可以使用 delete 方法。与上面讨论的 store 方法一样,delete 方法接受一个可调用对象,该对象接收传入的 HTTP 请求和与请求关联的模型实例

php
use Illuminate\Support\Facades\Storage;
use Laravel\Nova\Http\Requests\NovaRequest;

File::make('Attachment')
    ->disk('s3')
    ->delete(function (NovaRequest $request, $model, $disk, $path) {
        if (! $path) {
            return;
        }

        Storage::disk($disk)->delete($path);

        return [
            'attachment' => null,
            'attachment_name' => null,
            'attachment_size' => null,
        ];
    }),

如您在上面的示例中看到的,delete 回调返回一个键值对数组。这些键值对在保存到数据库之前映射到您的模型实例,允许您在存储文件后更新模型的一个或多个数据库列。通常,在删除字段时,您将在相关的数据库列中插入 NULL

可调用对象

当然,在闭包中执行所有文件删除逻辑会导致您的资源变得臃肿。为此,Nova 允许您将一个“可调用”对象传递给 delete 方法

php
File::make('Attachment')->delete(new DeleteAttachment);

可调用对象应该是一个简单的 PHP 类,只有一个 __invoke 方法

php
<?php

namespace App\Nova;

use Illuminate\Support\Facades\Storage;
use Laravel\Nova\Http\Requests\NovaRequest;

class DeleteAttachment
{
    /**
     * Delete the field's underlying file.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string|null  $disk
     * @param  string|null  $path
     * @return array
     */
    public function __invoke(NovaRequest $request, $model, $disk, $path)
    {
        if (! $path) {
            return;
        }

        Storage::disk($disk)->delete($path);

        return [
            'attachment' => null,
            'attachment_name' => null,
            'attachment_size' => null,
        ];
    }
}

自定义预览

默认情况下,Nova 将使用 Storage::url 方法来确定用于在资源详细信息页面和编辑表单上显示图像预览的 URL。但是,您可以使用 preview 方法自定义此 URL 的生成。

preview 方法接受一个可调用对象,该对象应该返回预览 URL。字段的底层列值作为第一个参数传递给可调用对象,而字段存储磁盘的名称作为第二个参数传递

php
use Laravel\Nova\Fields\Image;
use Illuminate\Support\Facades\Storage;

Image::make('Profile Photo')
    ->disk('public')
    ->preview(function ($value, $disk) {
        return $value
                    ? Storage::disk($disk)->url($value)
                    : null;
    }),

预览大小

默认情况下,Nova 资源详细信息页面将以 318 像素的宽度显示预览(“视网膜显示器”为 636 像素)。

自定义缩略图

默认情况下,Nova 将使用 Storage::url 方法来确定用于在资源索引页面和搜索结果(使用 Avatar 字段时)中显示缩略图预览的 URL。但是,您可以使用 thumbnail 方法自定义此 URL 的生成。

thumbnail 方法接受一个可调用函数,该函数应返回缩略图 URL。字段的底层列值作为第一个参数传递给可调用函数,而字段存储磁盘的名称作为第二个参数传递。

php
use Laravel\Nova\Fields\Image;
use Illuminate\Support\Facades\Storage;

Image::make('Profile Photo')
    ->disk('public')
    ->thumbnail(function ($value, $disk) {
        return $value
                    ? Storage::disk($disk)->url($value)
                    : null;
    }),

缩略图大小

默认情况下,Nova 将以 32 像素的宽度显示缩略图(“视网膜显示屏”为 64 像素)。

自定义下载

默认情况下,Nova 将使用 Storage::download 方法来确定用于下载文件的 文件和文件名。但是,您可以使用 download 方法自定义此 URL 的生成。download 方法接受一个可调用函数,该函数应返回您自己调用 Storage::download 方法的结果。

php
use Laravel\Nova\Fields\Image;
use Illuminate\Support\Facades\Storage;

Image::make('Profile Photo')
    ->disk('public')
    ->download(function ($request, $model, $disk, $value) {
        return Storage::disk($disk)->download($value, 'avatar');
    }),

使用临时 URL 下载大型文件

在 Laravel Vapor 等请求执行时间有限的环境中下载文件时,您可能需要将下载请求重定向到由 Laravel 的 Storage 门面生成的临时 URL。

php
use Laravel\Nova\Fields\Image;
use Illuminate\Support\Facades\Storage;

Image::make('Photo')
    ->disk('public')
    ->download(function ($request, $model, $disk, $value) {
        return redirect(
            Storage::disk($disk)->temporaryUrl($value, now()->addMinutes(5))
        );
    }),

自定义接受的文件类型

默认情况下,File 字段允许上传任何类型的文件;但是,您可以使用 acceptedTypes 方法自定义接受的文件类型。

php
File::make('Disk Image')->acceptedTypes('.dmg,.exe')

使用 acceptedTypes 方法时,Nova 会将 accept 属性添加到文件输入元素;因此,以下所有媒体类型都可以提供给 acceptedTypes 方法。

文件类型验证

由于 acceptedTypes 方法只执行客户端验证,因此您还应该使用服务器端验证规则验证文件类型。