Làm sao để import file excel có hàng chục nghìn rows vào database?

I. Lời mở đầu

Trong những năm gần đây, việc lưu trữ dữ liệu hay thông tin ra file excel là khá phổ biến. Và việc lưu lại thông tin dữ liệu lên máy chủ để sử dụng cho các mục đích khác nhau là rất cần thiết. Bài toán được nói đến ở đây là chúng ta làm thế nào để cùng lúc import một lượng dữ liệu lớn vào CSDL của máy chủ (trang web của chúng ta).

II. Bài toán

Import file excel có hàng chục, hàng trăm nghìn rows vào CSDL trang web.

III. Giải quyết

1. Cài đặt redis:

    sudo apt-get update
    sudo apt-get install build-essential tcl
    wget http://download.redis.io/releases/redis-stable.tar.gz

Sau khi download redis xong, giải nén redis:

    tar xzf redis-stable.tar.gz

Tiếp tục đi vào folder và make:

    cd redis-stable && make

Kiểm tra lại quá trình biên soajn:

    make test

Nếu không có gì lỗi thì bạn bắt đầu chạy cài đặt:

    sudo make install

Khởi động redis server:

    /usr/local/bin/redis-server

Và cuối cùng ping thử xem redis có chạy không:

    redis-cli ping
    PONG

2. Khởi tạo project:

    composer create-project --prefer-dist laravel/laravel project-excel

config cho queue:

    #-------------------------------------------------------------------------------
    # Worker
    #-------------------------------------------------------------------------------

    QUEUE_CONNECTION=redis

3. Bắt đầu nào:

Tạo một migration có tên là data, tại đây sẽ lưu trữ toàn bộ thông tin của file excel

    php artisan make:migration create_data_table

Thêm các fields cần lưu trữ: name, phoneaddress

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('data', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('phone');
            $table->string('address');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('data');
    }

Tiếp đó ta khởi tạo model Data:

    protected $table = 'data';

    protected $fillable = [
        'name',
        'phone',
        'address',
    ];

Cài đặt package laravel-excel:

    composer require maatwebsite/excel

Đăng ký ServiceProvider trong config/app.php

    'providers' => [
        /*
         * Package Service Providers...
         */
        Maatwebsite\Excel\ExcelServiceProvider::class,
    ]

Và cuối cùng chạy command để tạo ra file config/excel.php

    php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider"

Sau khi đã cài đăt xong package tiếp đến ta tiến hành viết code thôi nào =))

Tạo controller xử lý import:

  • Input: Request truyền vào là 1 file excel đã có sẵn data.
  • Output: Data được lưu trữ trong CSDL và thông báo thành công.
<?php

namespace App\Http\Controllers\Import;

use App\Imports\DataImport;
use App\Http\Controllers\Controller;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Http\Request;

class ImportQuestion extends Controller
{
    public function __invoke(Request $request)
    {
        Excel::import(new DataImport(), $request->file('file'));

        return response()->json([
            'status' => 'success',
        ], 200);
    }
}

Tạo một service xử lý file import: Phần này sẽ dùng packge laravel-excel để xử lý lấy ra data trong file excel. function collection sẽ chuyển các rows trong file excel về dạng collection từ đó ta có thể bóc tách được các thành phần.

        /**
         * @param Collection $collection
         */
        public function collection(Collection $userCollections)
        {
            foreach ($userCollections as $key => $user) {
                dispatch(new Import($user));
            }
        }

function startRows xử lý file excel từ hàng thứ 2 trở đi tức là bỏ phần header Name, Email, ...

    public function startRow(): int
    {
        return 2;
    }

function chunkSize lấy ra lần lượt tổng số rows trong mỗi lần đọc để tránh quá tải.

    public function chunkSize(): int
    {
        return 200;
    }

Cuối cùng ta sẽ có một class như sau:

<?php

namespace App\Imports;

use App\Jobs\Data\Import;
use App\Models\User;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Illuminate\Contracts\Queue\ShouldQueue;

class DataImport implements ToCollection, ShouldQueue, WithChunkReading, WithStartRow
{
    /**
     * @param Collection $collection
     */
    public function collection(Collection $userCollections)
    {
        foreach ($userCollections as $key => $user) {
            dispatch(new Import($user));
        }
    }

    // Bỏ phần header không cần thiết của file excel
    public function startRow(): int
    {
        return 2;
    }

   // Lấy lần lượt và Xử lý 200 rows liên tiếp (tránh việc lấy tất cả các rows sẽ khiến server quá tải)
    public function chunkSize(): int
    {
        return 200;
    }
}

Tạo một job có chạy queue để lưu trữ trong trường hợp này sẽ phải lưu một dữ liệu lượng lớn dữ liệu nên ta sẽ phải đẩy nó vào queue.

<?php

namespace App\Jobs\Data;

use App\Models\Data;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

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

    private $user;

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

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        Data::create([
            'name' => $this->user[0],
            'phone' => $this->user[1],
            'address' => $this->user[2],
        ]);
    }
}

Mở command và chạy queue rồi test thử ta sẽ thu được kết quả ahihi.

    php artisan queue:work

Input:

Output:

IV. Tạm kết

Qua bài viết chắc hẳn bạn đã có thể import một lượng lớn dữ liệu từ file excel vào CSDL rồi đúng không nào. Rất mong được sự góp ý từ mọi người. Ahihi =))


All Rights Reserved