Số tiền còn lại trong túi mình là điều tuyệt mật.
Khuyết Danh
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_idvàclient_secretvề 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).

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_tokentồn tại trên thanh url của browser, ta chỉ cần lấyaccess_tokenđó rồi gửi lên server của ta để lưu lại. - Khi đã có
access_tokentrê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\PostController@createPost'
)->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';
}
Bước 2: Thêm Redirect link
- Thêm Redirect link vào app trên imgur.com nếu chưa set tại https://imgur.com/account/settings/apps
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\PostController@createPost'
)->middleware('admin')
->middleware('check_access_token')
->name('createPost');
// https://blog.daovanhung.com/auth
Route::get(
'/auth',
'Blog\UserController@getAuth'
)->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_tokenvà 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\PostController@createPost'
)->middleware('admin')
->middleware('check_access_token')
->name('createPost');
// https://blog.daovanhung.com/auth
Route::get(
'/auth',
'Blog\UserController@getAuth'
)->middleware('admin')
->name('auth');
// https://blog.daovanhung.com/saveAuth
Route::post(
'/saveAuth',
'Blog\PostController@saveAuth'
)->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/.
