Laravel 提供了一個抽象的檔案系統,且這一切都要歸功於 Frank de Jonge 製作的 Flysystem PHP 套件。Laravel Flysystem 整合了本機檔案系統、Amazon S3 和 Rackspace 雲端儲存。更棒的是,能像使用 API 那樣輕易的切換這些儲存方式來面對各種系統。
檔案系統的設定檔就放在 config/filesystems.php
。在這個檔案中,你可以設定所有的「硬碟」。每個硬碟都代表著獨特的儲存驅動與儲存位置。已經將每個有支援的驅動設定範例都寫入這個設定檔中。所以你只需要修改其中的設定來選擇你的儲存偏好與憑證。
當然,你可以根據實際需求而設定多組硬碟,甚至使用相同的驅動。
public
硬碟用於存放會被公開存取的檔案。預設的 public
硬碟是使用 local
驅動並將檔案儲存到 storage/app/public
。為了要讓它們能夠從網頁上存取,你應該建立一個從 public/storage
到 storage/app/public
的符號連結。這個方式會讓你的公用可存取的檔案維持在一個目錄中,以便日後在使用像是 Envoyer 這種零停機部署系統時,可以輕易的共享整個部署。
你可以使用 storage:link
指令來建立連結符號:
php artisan storage:link
當然,一旦檔案被儲存並也建立了連結符號,就可以使用 asset
輔助函式來建立一個該檔案的 URL:
echo asset('storage/file.txt');
所有的操作是相對於設定檔中的「根目錄」設定進行。該目錄預設是 storage/app
。因此下列方法將把檔案儲存在 storage/app/file.txt
:
Storage::disk('local')->put('file.txt', 'Contents');
在使用 S3 或 Rackspace 驅動之前,你會需要透過 Composer 來安裝必要的套件:
- Amazon S3:
league/flysystem-aws-s3-v3 ~1.0
- Rackspace:
league/flysystem-rackspace ~1.0
S3 驅動設定資訊就放置於 config/filesystems.php
設定檔中。這個檔案有一個 S3 的驅動設定陣列的範例。你可以隨意的修改這個陣列來設定自己的 S3 設定與憑證。為了方便設定,這些環境變數會採用 AWS CLI 使用的命名慣例。
Laravel 的 Flysystem 也整合了 FTP 驅動。然而,框架預設的 filesystems.php
設定檔並不含 FTP 的簡單範例。如果你需要設定一個 FTP 檔案系統,你可以使用以下的範例設定:
'ftp' => [
'driver' => 'ftp',
'host' => 'ftp.example.com',
'username' => 'your-username',
'password' => 'your-password',
// 可選的 FTP 設定...
// 'port' => 21,
// 'root' => '',
// 'passive' => true,
// 'ssl' => true,
// 'timeout' => 30,
],
Laravel 的 Flysystem 也整合了 Rackspace 驅動。框架預設的 filesystems.php
設定檔並不含 Rackspace 的簡單範例。如果你需要設定一個 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',
],
Storage
facade 可被用於與任何設定的硬碟進行資料上的交換。例如,你可以在 Facade 上使用 put
方法將頭像儲存到預設的硬碟上。如果你不先使用 dist
方法而是直接呼叫 Storage
Facade,那麼該方法會自動傳入預設的硬碟。
use Illuminate\Support\Facades\Storage;
Storage::put('avatars/1', $fileContents);
如果你的應用程式要與多個硬碟交換資料,請使用在 Storage
Facade 上的 disk
方法來處理在特定硬碟上的檔案:
Storage::disk('s3')->put('avatars/1', $fileContents);
可以使用 get
方法來取得一個檔案的內容。檔案的原始字串內容會被這個方法所回傳。請記得,所有檔案路徑應該使用硬碟「根目錄」的相對位置:
$contents = Storage::get('file.jpg');
exists
方法可被用於檢查給定的檔案是否存在於硬碟上:
$exists = Storage::disk('s3')->exists('file.jpg');
你可以使用 url
方法來取得給定檔案的 URL。如果你正在使用 local
驅動,這通常只會在 /storage
後面加上給定的路徑,並回傳檔案的 URL。如果你正在使用 s3
或 rackspace
驅動,則會回傳完整的 URL:
use Illuminate\Support\Facades\Storage;
$url = Storage::url('file1.jpg');
{note} 請記得,如果你正使用
local
驅動,所有可被公開存取的檔案應該都放在storage/app/public
目錄中。此外,你也應該在public/storage
建立一個連結符號來指向storage/app/pbulic
目錄。
如果是使用 s3
或 rackspace
驅動來儲存檔案的話,你可以使用 temporaryUrl
方法來為給定的檔案建立一個臨時的 URL。這個方法接受一個路徑和一個用來指定 URL 有效期限的 DateTime
實例:
$url = Storage::temporaryUrl(
'file1.jpg', now()->addMinutes(5)
);
如果你想要使用 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('file1.jpg');
lastModified
方法會回傳最後一次修改檔案的 UNIX 時間戳記:
$time = Storage::lastModified('file1.jpg');
put
方法可被用於儲存硬碟上的原始檔案內容。你也可以將 PHP resource
傳入 put
方法,這會使用到 Flysystem 底層 stream 支援。強烈建議使用 stream 處理大型檔案。
use Illuminate\Support\Facades\Storage;
Storage::put('file.jpg', $contents);
Storage::put('file.jpg', $resource);
如果你想要 Laravel 來自動管理傳輸一個給定檔案到儲存的位置,你可以使用 putFile
或 putFileAs
方法。這個方法接受 Illuminate\Http\File
或 Illuminate\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 作為檔案名稱。該檔案的路徑會被 putFile
方法所回傳,以便將路徑和剛產生的檔案名稱存入資料庫中。
putFile
和 putFileAs
方法也接受一個用來指定儲存檔案的可見性的參數。如果你是將檔案儲存到像是 S3 的雲端硬碟,並希望檔案能夠被公開存取的話,這個用法會特別好用:
Storage::putFile('photos', new File('/path/to/photo'), 'public');
prepend
和 append
方法可以讓你寫入資料到檔案的開頭或結尾:
Storage::prepend('file.log', 'Prepended Text');
Storage::append('file.log', 'Appended Text');
copy
方法可被用來複製一個現有的檔案到硬碟上的新位置,至於 move
方法可被用於重新命名或移動一個現有的檔案到新位置:
Storage::copy('old/file1.jpg', 'new/file1.jpg');
Storage::move('old/file1.jpg', 'new/file1.jpg');
在網頁應用程式中,儲存檔案最常見的使用案例是儲存使用者上傳的檔案,像是個人照片、圖片和文件。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 作為檔案名稱。該檔案的路徑會被 store
方法所回傳,以便將路徑和剛產生的檔案名稱存入資料庫中。
你還能在 Storeage
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
方法的第二個參數:
$path = $request->file('avatar')->store(
'avatars/'.$request->user()->id, 's3'
);
在 Laravel 的 Flysystem 整合中,「可見性」是跨多個平台的檔案權限的抽象概念。檔案可被宣告為 public
或 private
。當一個檔案被宣告為 public
,就表示該檔案應該要能夠被其他人存取。例如,在使用 S3 驅動的時候,你可以接收 public
檔案的 URL。
你能透過設定 put
方法時,順便設定可見性:
use Illuminate\Support\Facades\Storage;
Storage::put('file.jpg', $contents, 'public');
如果該檔案已經被儲存,還是能透過 getVisibility
和 setVisibility
方法來接收和設定檔案的可見性:
$visibility = Storage::getVisibility('file.jpg');
Storage::setVisibility('file.jpg', 'public')
delete
方法接受一個檔案名稱或檔案名稱陣列,用來刪除硬碟上的檔案:
use Illuminate\Support\Facades\Storage;
Storage::delete('file.jpg');
Storage::delete(['file1.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 整合的 Flysystem 提供了幾個可馬上使用的「驅動」。然而,Flysystem 不受限於這些,還具有適用於其他儲存系統的連接器。你能在 Laravel 應用程式中使用其中一個額外的連接器來建立一個自訂的驅動。
為了設定自訂的檔案系統,你會需要一個 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['authorizationToken']
);
return new Filesystem(new DropboxAdapter($client));
});
}
/**
* 在容器中註冊綁定。
*
* @return void
*/
public function register()
{
//
}
}
extend
方法的第一個參數是驅動的名稱,第二個參數是用來接收 $app
和 $config
變數的必包。解析器閉包會回傳 League\Flysystem\Filesystem
實例。$config
變數包含了定義在 config/filesystems.php
對指定硬碟的設定。
一旦你建立了該服務容器到註冊擴充,你就能在 config/filesystems.php
設定檔中使用 dropbox
驅動。