Mở đầu

Khi phát triển một website, việc lưu trữ ảnh lên các server online thay vì server của chính mình giúp server của chúng ta tiết kiệm được rất nhiều bộ nhớ (disk).

Hiện nay có rất nhiều dịch vụ cung cấp lưu trữ ảnh online và free như google photos, imgur, flickr,...

Bài này mình xin trình bày một cách đơn giản để upload ảnh lên album của chính bạn trên imgur.

 

Chuẩn bị

  • Bạn cần đăng ký app trên imgur tại link https://api.imgur.com/oauth2/addclient
  • Khi đăng ký app thành công, bạn cần copy client_id và client_secret về project của bạn, có thể lưu trong file .env:
# .env
CLIENT_ID=123456789098765
CLIENT_SECRET=sfhaearfw49w8f9fy23e9efeihar3wr8wfif

 

Upload ảnh không cần lấy access_token

Khi đã có client_id, bạn có thể upload ảnh với tư cách là anonymous, nghĩa là ảnh bạn upload lên sẽ không liên kết với account của bạn.

Giả sử hàm trong controller xử lý việc lưu ảnh của bạn từ form upload lên là như sau:

public function saveImage()
{
    $data = request()->all();
    $image = $data['image'];
    // call service to upload image to imgur.com
    $imageUrl = ImgurService::uploadImage($image->getRealPath());
    // save your $imageUrl to DB if need
}

Ta tạo một service class lấy tên là ImgurService:

<?php

namespace App\Services;

use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client as GuzzleClient;

class ImgurService
{
    const END_POINT = 'https://api.imgur.com/3/image';

    public static function uploadImage($imagePath)
    {
        $client = new GuzzleClient();
        $request = $client->request(
            'POST',
            ImgurService::END_POINT,
            [
                'headers' => [
                    'Authorization' => "Client-ID ".env('CLIENT_ID'), // post as anonymous
                ],
                'form_params' => [
                    'image' => file_get_contents($imagePath)
                ]
            ]
        );
        $response = (string) $request->getBody();
        $jsonResponse = json_decode($response);
        return $jsonResponse->data->link; // return url of image
    }
}

 

Chỉ cần như thế là bạn đã upload được ảnh lên imgur.com thành công.

 

Upload ảnh lên album của chính mình

Để upload được ảnh lên album của chính mình hay của người nào khác, bạn cần phải có quyền để truy cập vào album đó. Kể cả bạn chỉ upload ảnh lên album của chính bạn chứ không phải của người khác, bạn cũng cần phải nhận được sự đồng ý của chính bạn để có thể truy cập vào album của chính bạn :)).

Để lấy được quyền truy cập vào album này, ta phải lấy được access_token từ imgur.com. Bình thường khi ứng dụng của bạn (gọi là ứng dụng A) muốn truy cập vào dữ liệu của người dùng B thì ứng dụng A phải đưa người dùng B tới trang yêu cầu cấp quyền của imgur.com cho ứng dụng A để ứng dụng A có thể lấy access_token của người dùng B để có thể truy cập thông tin của B trên imgur.com. (Bạn tìm hiểu thêm về Oauth2).

      get access_token of imgur oauth2

 

Trong các bưới gần cuối ở hình trên, với imgur API thì Service Provider trả về access_token luôn chứ không trả về Authorization Code nữa. Vì Service Provider trả về access_token luôn cho browser nên ta cần lấy được access_token này từ browser.

Nếu ứng dụng A phục vụ nhiều người dùng thì ta cần phải lưu trữ nhiều access_token ứng với nhiều người dùng vào DB, nhưng ở bài này sẽ trình bày một cách đơn giản việc lưu trữ access_token trong trường hợp ứng dụng của bạn chỉ có mỗi bạn cần sử dụng imgur.com.

Ý tưởng làm như sau:

  • Khi truy cập vào một url cần upload ảnh lên imgur thì check access_token đã tồn tại hay chưa. Nếu chưa tồn tại thì redirect người dùng tới trang yêu cầu cấp quyền truy cập.
  • Khi người dùng cấp quyền cho ứng dụng của bạn thì browser sẽ nhảy về một trang nào đó trên ứng dụng của bạn, tại trang này, access_token tồn tại trên thanh url của browser, ta chỉ cần lấy access_token đó rồi gửi lên server của ta để lưu lại.
  • Khi đã có access_token trên server của mình rồi thì bạn có thể thêm, sửa, xóa ảnh thoải mái bằng API.

 

Bước 1: Check access_token tồn tại trên server

  • Tạo file config/.credentials để lưu access_token:

Vì ứng dụng chỉ phục vụ cho một người nên ta có thể lưu access_token vào file mà không cần phải lưu vào DB, tất nhiên bạn hoàn toàn có thể lưu vào DB nếu muốn.

 

  • Check access_token ở route:
// routes/web.php

// https://blog.daovanhung.com/create-post
Route::get(
    '/create-post',
    'Blog\[email protected]'
)->middleware('admin')
->middleware('check_access_token')
->name('createPost');


Giả sử đường dẫn https://blog.daovanhung.com/create-post cần có phải upload ảnh lên album của mình.

Để đảm bảo việc chỉ admin mới có thể truy cập vào đường dẫn https://blog.daovanhung.com/create-post, ta thêm middleware('admin') vào như trên (có thể bỏ nếu muốn). middleware('check_access_token') để kiểm tra xem đã có access_token trên server hay chưa.

 

  • Đăng ký middleware check_access_token:
// console
php artisan make:middleware CheckAccessToken
// app/Http/Middleware/CheckAccessToken

<?php

namespace App\Http\Middleware;

use Closure;
use App\Services\ImgurService;

class CheckAccessToken
{
    public function handle($request, Closure $next)
    {
        // Lấy nội dung từ file config/.credentials
        $credential = file_get_contents(config_path('.credentials'));
        // Nếu chưa có access_token được lưu trữ trong file thì nhảy tới trang yêu cầu người dùng cấp quyền.
        if(strlen($credential) == 0) {
            return redirect()->away(ImgurService::imgurAuthUrl());
        }
        
        return $next($request); // Nếu đã có access_token trong .credentials rồi thì thực hiện như bình thường.
    }
}

 

Ở đoạn code trên, khi check .credentials ta chỉ check có nội dung hay không. Trên documents của imgur thì một access_token chỉ kéo dài tối đa 1 tháng và sau 1 tháng phải lấy refresh_token nhưng expires_in trả về khi lấy access_token là 10 năm nên chắc là access_token này có thể kéo dài tới 10 năm (1 tháng có lẽ do document vẫn chưa cập nhật, tham khảo ở https://community.imgur.com/t/expires-in-of-imgur-access-token/59742/3).

 

  • Đăng ký middleware vào kernel
// app/Http/Kernel.php

protected $routeMiddleware = [
    ...
    'check_access_token' => \App\Http\Middleware\CheckAccessToken::class
];

 

  • Thêm function imgurAuthUrl vào ImgurService
public static function ImgurAuthUrl()
{
    return 'https://api.imgur.com/oauth2/authorize'.'?client_id='.env('CLIENT_ID').'&response_type=token';
}

 

            thêm redirect link trong imgur access_token

Giả sử Redirect link là https://blog.daovanhung.com/auth.

 

  • Thêm route
// routes/web.php

// https://blog.daovanhung.com/create-post
Route::get(
    '/create-post',
    'Blog\[email protected]'
)->middleware('admin')
->middleware('check_access_token')
->name('createPost');


// https://blog.daovanhung.com/auth
Route::get(
    '/auth',
    'Blog\[email protected]'
)->middleware('admin')
->name('auth');

 

  • Thêm controller
// App/Http/Controllers/Blog/UserController.php

public function getAuth()
{
    return view('auth.imgur_auth');
}

 

  • Thêm view
// resources/views/auth/imgur_auth.blade.php

@extends('layouts.base')

Khi người dùng cấp quyền cho ứng dụng của bạn thì browser sẽ tự động nhảy về trang https://blog.daovanhung.com/auth với các thông số access_token, refresh_to và expires_in ví dụ như https://blog.daovanhung.com/auth#access_token=abcde&refresh_token=xyzw&expires_in=315360000

 

Bước 3: Lấy access_token và gửi lên server

  • Viết đoạn js để lấy access_token và nhúng vào trang https://blog.daovanhung.com/auth
// resources/views/auth/imgur_auth.blade.php

@extends('layouts.base')

@section('js')
<script defer>
    document.addEventListener("DOMContentLoaded", function(event) {
        fragments = window.location.href.split('#')[1] // fragments = 'access_token=abcde&refresh_token=xyzw&expires_in=315360000'
        $.post(window.location.origin + '/saveAuth', fragments)
            .done(function(data){
                console.log(data)
                // redirect về https://blog.daovanhung.com khi lưu thành công
                window.location.href = window.location.origin
            })
    });
</script>
@endsection

 

  • Thêm route:
// routes/web.php

// https://blog.daovanhung.com/create-post
Route::get(
    '/create-post',
    'Blog\[email protected]'
)->middleware('admin')
->middleware('check_access_token')
->name('createPost');

// https://blog.daovanhung.com/auth
Route::get(
    '/auth',
    'Blog\[email protected]'
)->middleware('admin')
->name('auth');

// https://blog.daovanhung.com/saveAuth
Route::post(
    '/saveAuth',
    'Blog\[email protected]'
)->middleware('admin')
->name('saveAuth');

 

  • Thêm controller:
// App/Http/Controllers/Blog/UserController.php

public function getAuth()
{
    return view('auth.imgur_auth');
}


public function saveAuth()
{
    $data = request()->all();
    $credentialFile = fopen(config_path('.credentials'), 'w');
    // lưu access_token vào file config/.credentials
    fwrite($credentialFile, $data['access_token']);
    fclose($credentialFile);
    return [
        'status' => 'success'
    ];
}

 

Bước 4: Sửa API upload ảnh lên album

Sau khi đã lưu thành công access_token vào config/.credentials thì bạn có thể truy cập lại vào https://blog.daovanhung.com/create-post  và upload ảnh bình thường lên imgur.com.

Để upload ảnh lên album thì bạn cần tạo album trên imgur.com rồi lấy id của album đó.

Ví dụ đường dẫn album của bạn là https://imgur.com/a/uXYdfg thì id chính là uXYdfg.

<?php

namespace App\Services;

use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client as GuzzleClient;

class ImgurService
{
    const END_POINT = 'https://api.imgur.com/3/image';
    const ALBUM_ID = 'uXYdyfg';

    public static function uploadImage($imagePath)
    {
        $accessToken = file_get_contents(config_path('.credentials'));
        $client = new GuzzleClient();
        $request = $client->request(
            'POST',
            ImgurService::END_POINT,
            [
                'headers' => [
                    //'Authorization' => "Client-ID ".env('CLIENT_ID'), // post as anonymous
                    'Authorization' => "Bearer ".$accessToken

                ],
                'form_params' => [
                    'image' => file_get_contents($imagePath),
                    'album' => ImgurService::ALBUM_ID
                ]
            ]
        );
        $response = (string) $request->getBody();
        $jsonResponse = json_decode($response);
        return $jsonResponse->data->link; // return url of image
    }
}

Lưu ý ở đoạn code trên thì Authorization của headers phải là access_token chứ không phải là client_id nữa.

Và trong form_params giờ có thêm param album.

 

Kết luận

Trên đây mình đã trình bày một cách đơn giản để upload được ảnh lên album trên imgur.com từ laravel.

Để sử dụng các API khác của imgur thì bạn có thể xem tại https://apidocs.imgur.com/.