Laravel cung cấp một abstraction filesystem mạnh mẽ nhờ package PHP Flysystem tuyệt vời của Frank de Jonge. Laravel Flysystem integration cung cấp các driver đơn giản để làm việc với các local filesystems, SFTP, và Amazon S3. Thậm chí, nó cũng rất đơn giản để chuyển đổi giữa các tùy chọn lưu trữ giữa máy phát triển local của bạn và máy chủ production vì API vẫn giống nhau cho mỗi hệ thống.
File cấu hình filesystem của Laravel được lưu tại config/filesystems.php
. Trong file này, bạn có thể cấu hình tất cả các filesystem "disks" của bạn. Mỗi disk sẽ được đại diện cho một driver lưu trữ với một vị trí lưu trữ cụ thể. Các cấu hình mẫu cho các driver được hỗ trợ cũng đã được khai báo sẵn vào trong file cấu hình vì vậy bạn có thể sửa cấu hình để đúng với tuỳ chọn lưu trữ của bạn và thông tin của chúng.
Driver local
tương tác với các file được lưu trữ local trên máy chủ đang chạy ứng dụng Laravel trong khi driver s3
sẽ được sử dụng để write vào dịch vụ lưu trữ đám mây S3 của Amazon.
{tip} Bạn có thể cấu hình bao nhiêu disk tùy ý của bạn và thậm chí có thể có nhiều disk sử dụng cùng một driver.
Khi sử dụng driver local
, tất cả các hoạt động của file đều liên quan đến thư mục root
được định nghĩa trong file cấu hình filesystems
của bạn. Mặc định, giá trị này được set cho thư mục storage/app
. Do đó, phương thức sau sẽ write vào storage/app/example.txt
:
use Illuminate\Support\Facades\Storage;
Storage::disk('local')->put('example.txt', 'Contents');
Disk public
có trong file cấu hình filesystems
của ứng dụng của bạn là dành cho các file có thể truy cập ở dạng công khai. Mặc định, public
disk sẽ sử dụng driver local
và lưu trữ các file này trong storage/app/public
.
Để làm cho các file này có thể truy cập từ web, bạn nên tạo một link liên kết ảo từ public/storage
đến storage/app/public
. Việc sử dụng quy ước thư mục này sẽ giúp cho các file có thể truy cập công khai của bạn ở trong một thư mục có thể dễ dàng chia sẻ qua mỗi lần deploy khi sử dụng các hệ thống deploy zero down-time như Envoyer.
Để tạo link liên kết ảo, bạn có thể sử dụng lệnh Artisan storage:link
:
php artisan storage:link
Một khi một file đã được lưu trữ và link liên kết ảo đã được tạo xong, bạn có thể tạo URL tới các file này bằng cách sử dụng helper asset
:
echo asset('storage/file.txt');
Bạn có thể cấu hình thêm các link ảo trong file cấu hình filesystems
của bạn. Mỗi link được cấu hình sẽ được tạo khi bạn chạy lệnh storage:link
:
'links' => [
public_path('storage') => storage_path('app/public'),
public_path('images') => storage_path('app/images'),
],
Trước khi sử dụng driver S3 hoặc SFTP, bạn sẽ cần cài đặt các package thích hợp thông qua Composer package manager:
composer require --with-all-dependencies league/flysystem-aws-s3-v3 "^1.0"
composer require league/flysystem-sftp "~1.0"
Ngoài ra, bạn có thể chọn cài đặt thêm cached adapter để tăng hiệu suất:
composer require league/flysystem-cached-adapter "~1.0"
Thông tin cấu hình driver S3 nằm trong file cấu hình config/filesystems.php
của bạn. File này chứa một mảng cấu hình mẫu cho driver S3. Bạn có thể tự do sửa mảng này với thông tin và cấu hình S3 của riêng bạn. Để thuận tiện, các biến môi trường đã được đặt tên khớp với quy ước đặt tên được sử dụng bởi AWS CLI.
Flysystem integration của Laravel hoạt động tốt với FTP; tuy nhiên, mặc định, một cấu hình mẫu không được thêm vào trong file cấu hình filesystems.php
của framework. Nếu bạn cần cấu hình một hệ thống file FTP, bạn có thể sử dụng cấu hình mẫu ở bên dưới:
'ftp' => [
'driver' => 'ftp',
'host' => env('FTP_HOST'),
'username' => env('FTP_USERNAME'),
'password' => env('FTP_PASSWORD'),
// Optional FTP Settings...
// 'port' => env('FTP_PORT', 21),
// 'root' => env('FTP_ROOT'),
// 'passive' => true,
// 'ssl' => true,
// 'timeout' => 30,
],
Flysystem tích hợp trong Laravel hoạt động tốt với SFTP; tuy nhiên, mặc định một cấu hình mẫu sẽ không có trong file cấu hình filesystems.php
của framework. Nếu bạn cần cấu hình một hệ thống filesystem SFTP, bạn có thể sử dụng cấu hình ví dụ ở bên dưới:
'sftp' => [
'driver' => 'sftp',
'host' => env('SFTP_HOST'),
// Settings for basic authentication...
'username' => env('SFTP_USERNAME'),
'password' => env('SFTP_PASSWORD'),
// Settings for SSH key based authentication with encryption password...
'privateKey' => env('SFTP_PRIVATE_KEY'),
'password' => env('SFTP_PASSWORD'),
// Optional SFTP Settings...
// 'port' => env('SFTP_PORT', 22),
// 'root' => env('SFTP_ROOT'),
// 'timeout' => 30,
],
Mặc định, file cấu hình filesystems
của ứng dụng sẽ chứa một cấu hình disk cho disk s3
. Ngoài việc sử dụng disk này để tương tác với Amazon S3, bạn cũng có thể sử dụng nó để tương tác với bất kỳ dịch vụ lưu trữ file nào tương thích S3 nào, chẳng hạn như MinIO hoặc DigitalOcean Spaces.
Thông thường, sau khi cập nhật thông tin đăng nhập của disk để khớp với thông tin đăng nhập của dịch vụ mà bạn đang sử dụng, bạn chỉ cần cập nhật giá trị của tùy chọn của cấu hình url
. Giá trị tùy chọn này thường được định nghĩa thông qua biến môi trường AWS_ENDPOINT
:
'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000'),
Để kích hoạt bộ nhớ cache cho một disk nhất định, bạn có thể thêm tuỳ chọn cache
vào các tùy chọn cấu hình của disk. Tùy chọn cache
sẽ phải là một mảng gồm các tùy chọn là tên cache store
, thời gian hết hạn expire
tính bằng giây và tiền tố prefix
:
's3' => [
'driver' => 's3',
// Other Disk Options...
'cache' => [
'store' => 'memcached',
'expire' => 600,
'prefix' => 'cache-prefix',
],
],
Facade Storage
có thể được sử dụng để tương tác với bất kỳ disk nào mà bạn cấu hình. Ví dụ, bạn có thể sử dụng phương thức put
trong facade này để lưu trữ hình đại diện cho một disk mà bạn muốn. Nếu bạn gọi các phương thức này trong facade Storage
mà không khai báo thêm phương thức disk
, thì câu lệnh này sẽ tự động được chuyển file đó đến disk mặc định:
use Illuminate\Support\Facades\Storage;
Storage::put('avatars/1', $content);
Nếu application của bạn tương tác với nhiều disk, thì bạn có thể sử dụng phương thức disk
trên facade Storage
để làm việc với các file cho một disk cụ thể:
Storage::disk('s3')->put('avatars/1', $content);
Thỉnh thoảng bạn có thể muốn tạo một disk trong khi ứng dụng chạy bằng cách sử dụng một cấu hình đã cho mà không có cấu hình đó ở trong file cấu hình filesystems
của ứng dụng của bạn. Để thực hiện điều này, bạn có thể truyền một mảng cấu hình cho phương thức build
của facade Storage
:
use Illuminate\Support\Facades\Storage;
$disk = Storage::build([
'driver' => 'local',
'root' => '/path/to/root',
]);
$disk->put('image.jpg', $content);
Phương thức get
có thể được sử dụng để lấy nội dung của file. Một chuỗi raw của nội dung file sẽ được phương thức trả về. Hãy nhớ rằng, tất cả các đường dẫn đến file phải được khai báo liên kết đến vị trí "root" của disk:
$contents = Storage::get('file.jpg');
Phương thức exists
có thể được sử dụng để xác định xem một file có tồn tại trên disk hay không:
if (Storage::disk('s3')->exists('file.jpg')) {
// ...
}
Phương thức missing
có thể được sử dụng để xác định xem file có bị thiếu trong disk hay không:
if (Storage::disk('s3')->missing('file.jpg')) {
// ...
}
Phương thức download
có thể được sử dụng để tạo một response buộc trình duyệt của người dùng tải xuống một file theo đường dẫn đã cho. Phương thức download
chấp nhận một tên file làm tham số thứ hai cho phương thức, tên file này sẽ hiển thị khi người dùng tải xuống. Cuối cùng, bạn có thể truyền một mảng HTTP header làm tham số thứ ba cho phương thức:
return Storage::download('file.jpg');
return Storage::download('file.jpg', $name, $headers);
Bạn có thể sử dụng phương thức url
để lấy ra URL đã cho cho một file. Nếu bạn đang sử dụng driver local
, điều này sẽ chỉ cần thêm /storage
vào đường dẫn đã cho và trả về một URL tương đối cho file. Nếu bạn đang sử dụng driver s3
, remote URL sẽ được trả về:
use Illuminate\Support\Facades\Storage;
$url = Storage::url('file.jpg');
Khi sử dụng driver local
, tất cả các file mà có thể truy cập ở dạng công khai thì nên được lưu trong thư mục storage/app/public
. Hơn nữa, bạn nên tạo một link liên kết ảo ở thư mục public/storage
để trỏ đến thư mục storage/app/public
.
{lưu ý} Khi sử dụng driver
local
, giá trị trả về củaurl
không phải là URL đã được encoded. Vì lý do này, mà chúng tôi khuyên bạn nên lưu trữ các file của bạn bằng các tên mà sẽ tạo ra URL hợp lệ.
Sử dụng phương thức temporaryUrl
, bạn có thể tạo ra các URL tạm cho các file được lưu trữ bằng driver s3
. Phương thức này chấp nhận một đường dẫn và một instance DateTime
để định nghĩa khi URL sẽ hết hạn:
use Illuminate\Support\Facades\Storage;
$url = Storage::temporaryUrl(
'file.jpg', now()->addMinutes(5)
);
Nếu bạn cần chỉ định thêm một S3 request parameters, bạn có thể truyền một mảng request parameter làm tham số thứ ba cho phương thức temporaryUrl
:
$url = Storage::temporaryUrl(
'file.jpg',
now()->addMinutes(5),
[
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename=file2.jpg',
]
);
Nếu bạn cần tùy chỉnh cách mà các URL tạm được tạo ra cho một disk lưu trữ cụ thể, bạn có thể sử dụng phương thức buildTemporaryUrlsUsing
. Ví dụ: điều này có thể hữu ích nếu bạn có một controller cho phép người dùng tải xuống các file được lưu trữ thông qua disk mà thường không hỗ trợ URL tạm. Thông thường, phương thức này nên được gọi từ phương thức boot
của một service provider:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Storage::disk('local')->buildTemporaryUrlsUsing(function ($path, $expiration, $options) {
return URL::temporarySignedRoute(
'files.download',
$expiration,
array_merge($options, ['path' => $path])
);
});
}
}
Nếu như bạn muốn định nghĩa thêm host cho các URL mà được tạo ra khi đang dùng facade Storage
, bạn có thể thêm tùy chọn url
vào mảng cấu hình của disk:
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
Ngoài việc đọc và ghi file, Laravel cũng cung cấp thông tin về các file đó. Ví dụ, phương thức size
có thể được sử dụng để lấy ra kích thước của một file theo đơn vị byte:
use Illuminate\Support\Facades\Storage;
$size = Storage::size('file.jpg');
Phương thức lastModified
trả về một UNIX timestamp về lần cuối cùng mà file được sửa:
$time = Storage::lastModified('file.jpg');
Bạn có thể sử dụng phương thức path
để lấy ra một đường dẫn cho một file nhất định. Nếu bạn đang sử dụng driver local
, điều này sẽ trả về đường dẫn tuyệt đối tới file. Nếu bạn đang sử dụng driver s3
, phương thức này sẽ trả về đường dẫn tương đối tới file trong bộ chứa S3:
use Illuminate\Support\Facades\Storage;
$path = Storage::path('file.jpg');
Phương thức put
có thể được sử dụng để lưu trữ một nội dung của file lên disk. Bạn cũng có thể truyền một PHP resource
đến phương thức put
, phương thức này sẽ sử dụng support stream của Flysystem. Hãy nhớ rằng, tất cả các đường dẫn đến file phải được khai báo liên kết đến vị trí "root" mà đã được cấu hình cho disk:
use Illuminate\Support\Facades\Storage;
Storage::put('file.jpg', $contents);
Storage::put('file.jpg', $resource);
Streaming một file đến storage giúp giảm đáng kể mức sử dụng bộ nhớ. Nếu bạn muốn Laravel tự động quản lý việc streaming một file đã cho đến một vị trí lưu trữ của bạn, bạn có thể sử dụng phương thức putFile
hoặc putFileAs
. Phương thức này chấp nhận một instance Illuminate\Http\File
hoặc một Illuminate\Http\UploadedFile
và sẽ tự động stream file đó đến vị trí mong muốn của bạn:
use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
// Automatically generate a unique ID for filename...
$path = Storage::putFile('photos', new File('/path/to/photo'));
// Manually specify a filename...
$path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');
Có một vài điều quan trọng cần phải lưu ý về phương thức putFile
. Hãy lưu ý rằng chúng ta chỉ khai báo đến của tên thư mục và không phải khai báo đến tên của file. Mặc định, phương thức putFile
sẽ tạo một unique ID để làm tên file. Phần đuôi mở rộng của file sẽ được xác định bằng cách kiểm tra kiểu MIME của file. Đường dẫn đến file sẽ được trả về bởi phương thức putFile
để bạn có thể lưu trữ đường dẫn đó vào trong cơ sở dữ liệu của bạn, đường dẫn này cũng chứa cả tên file đã được tạo.
Các phương thức putFile
và putFileAs
cũng chấp nhận một than số để khai báo "visibility" của file được lưu trữ. Điều này đặc biệt hữu ích nếu bạn đang lưu trữ file trên một cloud disk như Amazon S3 và muốn file này có thể truy cập công khai thông qua URL được generate :
Storage::putFile('photos', new File('/path/to/photo'), 'public');
Các phương thức prepend
và append
cho phép bạn ghi vào đầu dòng hoặc cuối dòng của một file:
Storage::prepend('file.log', 'Prepended Text');
Storage::append('file.log', 'Appended Text');
Phương thức copy
có thể được sử dụng để sao chép một file hiện có sang một vị trí mới trên disk, trong khi phương thức move
có thể được sử dụng để đổi tên hoặc di chuyển một file hiện có sang một vị trí mới:
Storage::copy('old/file.jpg', 'new/file.jpg');
Storage::move('old/file.jpg', 'new/file.jpg');
Trong các application web, một trong những trường hợp hay sử dụng nhất cho lưu trữ file là lưu trữ các file được upload từ người dùng như photo và document. Laravel giúp lưu trữ dễ dàng các file được upload bằng cách sử dụng phương thức store
trên một instance file upload. Gọi phương thức store
với đường dẫn mà bạn muốn lưu trữ file vào:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserAvatarController extends Controller
{
/**
* Update the avatar for the user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function update(Request $request)
{
$path = $request->file('avatar')->store('avatars');
return $path;
}
}
Có một vài điều quan trọng cần lưu ý về ví dụ này. Hãy lưu ý rằng chúng ta chỉ khai báo đến tên thư mục, không phải đến tên file. Mặc định, phương thức store
sẽ tạo một unique ID để làm tên file. Phần đuôi mở rộng của file sẽ được xác định bằng cách kiểm tra kiểu MIME của file. Đường dẫn đến file sẽ được trả về từ phương thức store
, để bạn có thể lưu trữ đường dẫn đó vào trong cơ sở dữ liệu của bạn, đường dẫn này cũng chứa cả tên file đã được tạo.
Bạn cũng có thể gọi phương thức putFile
trên facade Storage
để thực hiện thao tác lưu trữ file giống như ví dụ trên:
$path = Storage::putFile('avatars', $request->file('avatar'));
Nếu bạn không muốn tên file được tự động gán cho file, bạn có thể sử dụng phương thức storeAs
, nhận vào một đường dẫn, một tên file và một tên disk (tùy chọn) làm tham số của nó:
$path = $request->file('avatar')->storeAs(
'avatars', $request->user()->id
);
Bạn cũng có thể sử dụng phương thức putFileAs
trên facade Storage
, để thực hiện thao tác lưu trữ file giống như ví dụ trên:
$path = Storage::putFileAs(
'avatars', $request->file('avatar'), $request->user()->id
);
{note} Các ký tự unicode không in được hoặc không hợp lệ sẽ bị tự động xóa khỏi đường dẫn đến file. Vì vậy, bạn có thể muốn làm sạch đường dẫn đến file của bạn trước khi truyền chúng đến các phương thức lưu trữ file của Laravel. Đường dẫn đến file có thể được chuẩn hóa bằng phương thức
League\Flysystem\Util::normalizePath
.
Mặc định, phương thức store
của file được upload sẽ sử dụng disk mặc định. Nếu bạn muốn chỉ định một disk khác, hãy truyền tên disk làm tham số thứ hai cho phương thức store
:
$path = $request->file('avatar')->store(
'avatars/'.$request->user()->id, 's3'
);
Nếu bạn đang sử dụng phương thức storeAs
, bạn có thể truyền tên disk làm tham số thứ ba cho phương thức:
$path = $request->file('avatar')->storeAs(
'avatars',
$request->user()->id,
's3'
);
Nếu bạn muốn lấy tên gốc và phần mở rộng của một file đã được tải lên, bạn có thể làm như sau bằng cách sử dụng các phương thức getClientOriginalName
và getClientOriginalExtension
:
$file = $request->file('avatar');
$name = $file->getClientOriginalName();
$extension = $file->getClientOriginalExtension();
Tuy nhiên, hãy nhớ rằng các phương thức getClientOriginalName
và getClientOriginalExtension
sẽ được coi là không an toàn vì tên file và phần mở rộng có thể bị người dùng ác ý giả mạo. Vì lý do này, bạn nên sử dụng các phương thức hashName
và extension
để lấy tên và phần mở rộng cho file đã được tải lên:
$file = $request->file('avatar');
$name = $file->hashName(); // Generate a unique, random name...
$extension = $file->extension(); // Determine the file's extension based on the file's MIME type...
Trong Flysystem integration của Laravel, "visibility" là một trừu tượng về các quyền của file trên nhiều nền tảng. Các file có thể được khai báo public
hoặc private
. Khi một file được khai báo public
, bạn đã cho phép file đó có thể truy cập được từ người dùng khác. Ví dụ: khi sử dụng driver S3, bạn có thể lấy được URL cho các file public
.
Bạn có thể set visibility khi viết file thông qua phương thức put
:
use Illuminate\Support\Facades\Storage;
Storage::put('file.jpg', $contents, 'public');
Nếu file đã được lưu trữ, visibility của nó có thể được lấy ra và thiết lập thông qua các phương thức getVisibility
và setVisibility
:
$visibility = Storage::getVisibility('file.jpg');
Storage::setVisibility('file.jpg', 'public');
Khi tương tác với các file được upload, bạn có thể sử dụng các phương thức storePublicly
hoặc storePubliclyAs
để lưu trữ các file đã được upload với chế độ public
:
$path = $request->file('avatar')->storePublicly('avatars', 's3');
$path = $request->file('avatar')->storePubliclyAs(
'avatars',
$request->user()->id,
's3'
);
Khi sử dụng driver local
, thư mục public
visibility sẽ được chuyển thành 0755
cho thư mục và 0644
cho file. Bạn có thể sửa các quyền này trong file cấu hình filesystems
của bạn:
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'permissions' => [
'file' => [
'public' => 0644,
'private' => 0600,
],
'dir' => [
'public' => 0755,
'private' => 0700,
],
],
],
Phương thức delete
chấp nhận một tên file hoặc một mảng các tên file để xóa:
use Illuminate\Support\Facades\Storage;
Storage::delete('file.jpg');
Storage::delete(['file.jpg', 'file2.jpg']);
Nếu cần, bạn có thể khai báo disk mà file đó sẽ bị xóa:
use Illuminate\Support\Facades\Storage;
Storage::disk('s3')->delete('path/file.jpg');
Phương thức files
sẽ trả về một mảng của tất cả các file có trong một thư mục nhất định. Nếu bạn muốn lấy danh sách tất cả các file có trong một thư mục bao gồm cả các thư mục con, bạn có thể sử dụng phương thức allFiles
:
use Illuminate\Support\Facades\Storage;
$files = Storage::files($directory);
$files = Storage::allFiles($directory);
Phương thức directories
trả về một mảng gồm tất cả các thư mục có trong một thư mục đã cho. Ngoài ra, bạn có thể sử dụng phương thức allDirectories
để lấy danh sách tất cả các thư mục có trong một thư mục đã cho và cả các thư mục con của nó:
$directories = Storage::directories($directory);
$directories = Storage::allDirectories($directory);
Phương thức makeDirectory
sẽ tạo mới một thư mục, bao gồm cả thư mục con nếu cần thiết:
Storage::makeDirectory($directory);
Cuối cùng, phương thức deleteDirectory
có thể được sử dụng để xóa một thư mục và tất cả các file trong nó:
Storage::deleteDirectory($directory);
Flysystem tích hợp của Laravel cung cấp hỗ trợ cho một số "driver" mặc đinh; tuy nhiên, Flysystem không chỉ giới hạn ở những điều này mà còn có bộ chuyển đổi cho nhiều hệ thống lưu trữ khác. Bạn có thể tạo driver tùy biến nếu bạn muốn sử dụng một trong những bộ chuyển đổi đó vào trong ứng dụng Laravel của bạn.
Để định nghĩa một tuỳ biến filesystem, bạn sẽ cần một bộ chuyển đổi Flysystem. Hãy thêm một bộ chuyển đổi Dropbox được cộng đồng phát triển vào trong dự án của bạn:
composer require spatie/flysystem-dropbox
Tiếp theo, bạn có thể đăng ký driver trong phương thức boot
của một trong những service providers trong ứng dụng của bạn. Để thực hiện điều này, bạn nên sử dụng phương thức extend
của facade Storage
:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client as DropboxClient;
use Spatie\FlysystemDropbox\DropboxAdapter;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Storage::extend('dropbox', function ($app, $config) {
$client = new DropboxClient(
$config['authorization_token']
);
return new Filesystem(new DropboxAdapter($client));
});
}
}
Tham số đầu tiên của phương thức extend
là tên của driver và tham số thứ hai là một closure nhận các biến $app
và $config
. Closure này cần trả về một instance của League\Flysystem\Filesystem
. Biến $config
sẽ chứa các giá trị được định nghĩa trong file config/filesystems.php
cho disk mà bạn đang khai báo.
Khi bạn đã tạo và đăng ký xong service provider, bạn có thể sử dụng driver dropbox
trong file cấu hình config/filesystems.php
.
entry