Cách mã hóa và tải các tệp lớn lên web trong Laravel

1. Giới thiệu

  • XIn chào mọi người hôm này mình có 1 bài viết về mã hóa tệp lớn khi upload dữ liệu lên hệ thống . Hôm nay mình muốn chỉ cho bạn cách tải lên và mã hóa các tệp lớn lên hệ thống , sử dụng package Laravel và FileVault .
  • Đầu tiên, mình sẽ giải thích một vài khái niệm và phương pháp mà chúng ta sẽ sử dụng .

2. Streaming Files to Amazon S3

Laravel đã cung cấp tất cả các công cụ cần thiết để tải tệp lên Amazon S3. Nếu bạn không biết cách thực hiện điều đó, hãy xem các hàm putFileputFileAs functions on the Storage facad . Với bất kỳ chức năng nào trong hai chức năng này, Laravel sẽ tự động quản lý việc truyền tệp đến một vị trí lưu trữ, chẳng hạn như Amazon S3. Tất cả bạn cần làm nó sẽ kiểu như này =)) :

Storage::disk('s3')->putFile('photos', new File('/path/to/photo'));

Truyền tệp đến S3 có thể mất nhiều thời gian, tùy thuộc vào tốc độ mạng. Ngay cả khi các hàm putFileputFileAs truyền phát tệp theo phân đoạn thì tiêu tốn rất nhiều bộ nhớ . Quá trình có thể mất rất nhiều thời gian để hoàn thành, gây ra thời gian chờ. Đó là lý do tại sao nó khuyến khích chúng ta sử dụng queued jobs.

3. Using Queued Jobs

  • Hàng đợi cho phép bạn trì hoãn việc xử lý một tác vụ tốn thời gian. Trì hoãn các tác vụ tốn thời gian này sẽ tăng tốc đáng kể các yêu cầu web đến ứng dụng của bạn .
  • Chúng ta sẽ sử dụng hai công việc được ném vào queue riêng biệt, một cái để mã hóa tệp và một cái để tải tệp được mã hóa lên Amazon S3.
  • Trong Laravel, bạn có thể xâu chuỗi các queue để các công việc sẽ chạy theo trình tự. Bằng cách này, chúng ta có thể bắt đầu tải tệp lên S3 ngay sau khi tệp được mã hóa.

4. Configure Amazon S3

  • Trước tiên, bạn sẽ cần cấu hình S3 ở phía Amazon và tạo một nhóm nơi chúng ta sẽ lưu trữ các tệp được mã hóa. link config bạn có thể thảm khảo ở đây và ném vào file .env nhé .
  • Theo document của Laravel, chúng ta cũng cần cài đặt Flysystem adapter package thông qua composer :
composer require league/flysystem-aws-s3-v3
  • Tiếp theo mình add 1 package cached adapter
composer require league/flysystem-cached-adapter

5. Creating Queueable Jobs

  • đầu tiên sẽ make 2 file queue như đã nói ở trên là mỗi 1 queue sẽ phục vụ cho 1 việc riêng biệt
php artisan make:job EncryptFile
php artisan make:job MoveFileToS3
  • sẽ có 2 file queue đc tạo ra ở đây app/Http/Jobs
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use SoareCostin\FileVault\Facades\FileVault;

class EncryptFile implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $filename;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($filename)
    {
        $this->filename = $filename;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        FileVault::encrypt($this->filename);
    }
}
<?php

namespace App\Jobs;

use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Http\File;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;

class MoveFileToS3 implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $filename;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($filename)
    {
        $this->filename = $filename . '.enc';
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // Upload file to S3
        $result = Storage::disk('s3')->putFileAs(
            '/',
            new File(storage_path('app/' . $this->filename)),
            $this->filename
        );

        // Forces collection of any existing garbage cycles
        // If we don't add this, in some cases the file remains locked
        gc_collect_cycles();

        if ($result == false) {
            throw new Exception("Couldn't upload file to S3");
        }

        // delete file from local filesystem
        if (!Storage::disk('local')->delete($this->filename)) {
            throw new Exception('File could not be deleted from the local filesystem ');
        }
    }
}
  • Như bạn có thể thấy, việc EncryptFile rất đơn giản - chúng ta chỉ sử dụng gói FileVault để mã hóa tệp và lưu nó vào cùng thư mục có cùng tên .

  • Thay vì mã hóa bằng FileVault bằng phương thức store, chúng ta dispatch nó vào hàng đợi như sau :

EncryptFile::withChain([
    new MoveFileToS3($filename),
])->dispatch($filename);
  • Tiếp theo, trong method index , mình gửi chúng ra cho người dùng :
$localFiles = Storage::files(‘files/. auth()->user()->id);
$s3Files = Storage::disk(‘s3’)->files(‘files/. auth()->user()->id);

return view(‘home’, compact(‘localFiles’, ‘s3Files’));
  • Mình cũng cập nhật downloadFile của mình, chỉ định rằng muốn tải xuống và truyền tệp từ S3 thay vì hệ thống tệp cục bộ. Chúng tôi chỉ xâu chuỗi một cuộc gọi đĩa (‘s3) đến cả hai mặt tiền Storage và FileVault.

  • đây là những gì mình xử lý trong Controller :

<?php

namespace App\Http\Controllers;

use App\Jobs\EncryptFile;
use App\Jobs\MoveFileToS3;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use SoareCostin\FileVault\Facades\FileVault;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
        $localFiles = Storage::files('files/' . auth()->user()->id);
        $s3Files = Storage::disk('s3')->files('files/' . auth()->user()->id);

        return view('home', compact('localFiles', 's3Files'));
    }

    /**
     * Store a user uploaded file
     *
     * @param  \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        if ($request->hasFile('userFile') && $request->file('userFile')->isValid()) {
            $filename = Storage::putFile('files/' . auth()->user()->id, $request->file('userFile'));

            // check if we have a valid file uploaded
            if ($filename) {
                EncryptFile::withChain([
                    new MoveFileToS3($filename),
                ])->dispatch($filename);
            }
        }

        return redirect()->route('home')->with('message', 'Upload complete');
    }

    /**
     * Download a file
     *
     * @param  string  $filename
     * @return \Illuminate\Http\Response
     */
    public function downloadFile($filename)
    {
        // Basic validation to check if the file exists and is in the user directory
        if (!Storage::disk('s3')->has('files/' . auth()->user()->id . '/' . $filename)) {
            abort(404);
        }

        return response()->streamDownload(function () use ($filename) {
            FileVault::disk('s3')->streamDecrypt('files/' . auth()->user()->id . '/' . $filename);
        }, Str::replaceLast('.enc', '', $filename));
    }

}
  • Điều cuối cùng chúng ta cần làm là cập nhật home.blade.php, để chúng ta có thể hiển thị không chỉ các tệp người dùng đã được mã hóa và được lưu trữ vào S3 mà cả các tệp đang được mã hóa và tải lên S3.
<h4>Your files</h4>
<ul class="list-group">
    @forelse ($s3Files as $file)
        <li class="list-group-item">
            <a href="{{ route('downloadFile', basename($file)) }}">
                {{ basename($file) }}
            </a>
        </li>
    @empty
        <li class="list-group-item">You have no files</li>
    @endforelse
</ul>

@if (!empty($localFiles))
<hr />
<h4>Uploading and encrypting...</h4>
<ul class="list-group">
    @foreach ($localFiles as $file)
        <li class="list-group-item">
            {{ basename($file) }}
        </li>
    @endforeach
</ul>
@endif

6. Queue Configuration

  • Nếu bạn không thực hiện bất kỳ thay đổi nào đối với cấu hình hàng đợi, rất có thể bạn đang sử dụng đồng bộ (sync) được đặt mặc định trong Laravel. queue config
  • câu lệnh tạo model queue
php artisan queue:table
php artisan migrate 
  • sau đó chúng ta sẽ update QUEUE_CONNECTION trong file .env .
QUEUE_CONNECTION=database
  • Tiếp theo chúng ta cần start queue :
php artisan queue:work --tries=3

7 Tổng kết

  • Bây giờ để kiểm tra những thay đổi, khi bạn tải lên một tập tin, bạn sẽ thấy rằng tập tin đó được hiển thị ngay lập tức trong phần Tải lên và mã hóa mã hóa trực tuyến.

All Rights Reserved