Bạn nào muốn liên hệ trực tiếp mình để hỏi về roadmap qua Nhật làm Dev hoặc BrSE thì contact mình qua zalo nhé: 0379302361 hoặc Facebook nhé.
Hi, I'm Tuan, a Full-stack Web Developer from Tokyo 😊. Follow my blog to not miss out on useful and interesting articles in the future.
1. Giới thiệu
Hé lô các bạn! Hôm nay, mình sẽ giới thiệu cho các bạn về thư viện S3Local mà mình đã tự tay xây dựng. S3Local giả lập lại AWS S3 trên NodeJS mà không cần dựa vào thư viện bên thứ 3. Có 2 lý do mà mình đã quyết định tự làm nó: đầu tiên, mình không muốn phụ thuộc vào thư viện bên thứ 3 vì không biết chúng có thể mang lại những lỗ hổng bảo mật không kiểm soát được. Thứ hai, đơn giản là vì mình thích làm thế. Điều này cũng giúp mình nắm bắt được rõ hơn cách hoạt động của S3 thông qua một ví dụ siêu đơn giản (Tất nhiên trong thực tế nó sẽ fancy hơn vậy gấp 100 lần - tuy nhiên learn mà cứ giễ mà phang trước đã).
1.1 Cài đặt ban đầu
Không lạ gì với ai đang làm việc với NodeJS, chúng ta sẽ bắt đầu bằng cách khởi tạo một project mới. Sử dụng command sau trong terminal của bạn:
npm init -y
Cuối cùng, thêm dòng "type": "module"
vào file package.json
của bạn. Và nó sẽ trông như thế này:
{ "name": "s3local", "version": "1.0.0", "description": "", "main": "app.js", "type": "module", "scripts": { "dev": "node app.js" }, "keywords": [], "author": "", "license": "ISC"
}
2. S3Local thư viện của chúng ta
2.1 Tổng quan
Thư viện S3Local gồm 2 file chính: s3_local.js
và app.js
.
2.2 File s3_local.js
Đầu tiên, hãy cùng tìm hiểu về file s3_local.js
.
Đây là nơi mà mình đã định nghĩa ra S3Local cùng với các command để thao tác với các bucket. Mình đã áp dụng Command Design Pattern vào đây để tạo ra một cấu trúc rõ ràng và giúp dễ dàng mở rộng hơn trong tương lai.
import fs from "fs";
import path from "path"; class Command { constructor(bucket) { this.bucket = bucket; } async execute() { throw new Error("Execute method must be overridden in the child class"); }
} class GetObjectCommand extends Command { async execute(s3Local) { return await s3Local.get(this.bucket); }
} class PutObjectCommand extends Command { async execute(s3Local, file) { return await s3Local.put(this.bucket, file); }
} class DeleteObjectCommand extends Command { async execute(s3Local) { return await s3Local.delete(this.bucket); }
} class S3Local { constructor(config) { this.path = config.path; this.buckets = []; } getBucketPath(bucket) { return bucket.Bucket || "" + bucket.Key || ""; } async get(bucket) { const filePath = path.join(this.path, this.getBucketPath(bucket)); return await fs.promises.readFile(filePath, "utf-8"); } async put(bucket, file) { const filePath = path.join(this.path, this.getBucketPath(bucket)); await fs.promises.writeFile(filePath, file); this.buckets.push(bucket); } async delete(bucket) { const filePath = path.join(this.path, this.getBucketPath(bucket)); await fs.promises.unlink(filePath); this.buckets = this.buckets.filter((b) => this.getBucketPath(b) !== this.getBucketPath(bucket)); } async send(command, file) { return await command.execute(this, file); }
} export { GetObjectCommand, PutObjectCommand, DeleteObjectCommand, S3Local };
Tiếp theo mình sẽ giải thích kỹ từng thành phần của thư viện nhé
2.2.1 Class Command
Class Command là class cha, nó chứa phương thức execute()
mà các class con sẽ phải ghi đè để thực hiện các thao tác khác nhau.
2.2.2 Class GetObjectCommand, PutObjectCommand, DeleteObjectCommand
Đây là các class con, mỗi class sẽ ghi đè phương thức execute()
để thực hiện việc lấy dữ liệu từ bucket (GetObjectCommand), đặt dữ liệu vào bucket (PutObjectCommand), hoặc xóa bucket (DeleteObjectCommand).
2.2.3 Class S3Local
Class S3Local chính là trung tâm của thư viện mình viết. Nó chứa các phương thức cơ bản như get()
, put()
, và delete()
. Bên cạnh đó, mình còn thêm phương thức send()
để thực hiện các command mà chúng ta đã định nghĩa ở trên.
3. Sử dụng thư viện S3Local
Mình đã giới thiệu sơ qua về S3Local, bây giờ hãy cùng xem cách sử dụng nó như thế nào qua file app.js
.
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Local } from "./s3_local.js"; // Tạo một instance của S3Local
const config = { path: ".", // Đường dẫn thư mục lưu trữ dữ liệu
};
const s3Local = new S3Local(config); // Tạo một bucket mới
const bucket = { Key: "file của tôi.txt",
}; // Thực hiện các command
(async () => { try { // Ghi dữ liệu vào bucket const fileContent = "Nội dung file test."; await s3Local.send(new PutObjectCommand(bucket), fileContent); // Đọc dữ liệu từ bucket const result = await s3Local.send(new GetObjectCommand(bucket)); console.log("Ghi và đọc dữ liệu thành công >> Nội dung file là:", result); // Xóa bucket await s3Local.send(new DeleteObjectCommand(bucket)); // Kiểm tra xem bucket đã bị xóa thành công hay chưa try { await s3Local.send(new GetObjectCommand(bucket)); } catch (error) { console.log("Xóa Bucket thành công."); } // Ghi dữ liệu Backup await s3Local.send(new PutObjectCommand({...bucket, Key: 'file backup.txt'}), fileContent + '.... Backup'); } catch (error) { console.error("Lỗi zồi:", error); }
})();
3.1 Khởi tạo S3Local
Đầu tiên, chúng ta sẽ khởi tạo một instance của S3Local với config chỉ ra đường dẫn đến thư mục mà chúng ta sẽ lưu trữ dữ liệu.
3.2 Thực hiện các command
Sau khi đã có S3Local, giờ đến lúc chúng ta sẽ tận dụng nó để thực hiện các thao tác với dữ liệu. Qua hàm send()
, chúng ta có thể gửi các command khác nhau như PutObjectCommand
, GetObjectCommand
, DeleteObjectCommand
để thực hiện việc đặt, lấy, hoặc xóa dữ liệu từ bucket.
4. Kết luận
Đó là tất cả những gì về S3Local. Mình hy vọng rằng thông qua việc tự tay xây dựng một thư viện như S3Local, các bạn đã hiểu rõ hơn về cách thức hoạt động của AWS S3 cũng như việc áp dụng Command Design Pattern trong việc thiết kế thư viện. Chắc chắn còn nhiều điều mà mình chưa kịp chia sẻ trong bài viết này. Không nói đến thư viện aws s3 real, thư viện ở trên khi áp dụng vào dự án thực tế hiện tại của mình thì cũng sẽ được xào nấu lại khá nhiều cho phù hợp với logic dự án. Tuy nhiên về mặt ý tưởng nó vẫn như vậy. Chúc các bạn thành công.
And Finally
As always, I hope you enjoyed this article and got something new. Thank you and see you in the next articles!
If you liked this article, please give me a like and subscribe to support me. Thank you. 😊
Mình có tạo 1 series để trả lời những câu hỏi mà các bạn đã liên lạc và hỏi mình. Vì câu hỏi khá nhiều nên mình sẽ trả lời dần dần và add vào series này nè. Link tham khảo: https://viblo.asia/s/chuyen-muc-tra-loi-cau-hoi-cuoc-song-dev-tai-nhut-bon-PwlVmR7Z45Z
Bạn nào muốn liên hệ trực tiếp mình để hỏi về roadmap qua Nhật làm Dev hoặc BrSE thì contact mình qua zalo nhé: 0379302361 hoặc Facebook nhé.