Image Archive System là hệ thống lưu trữ và quản lý hình ảnh hiệu năng cao, được thiết kế để tối ưu chi phí và hiệu suất. Hệ thống sử dụng Cloudinary làm nơi lưu trữ ảnh, Cloudflare Pages làm nền tảng triển khai, và tích hợp Jetpack để tối ưu hóa hình ảnh. Với khả năng phân phối tải lên nhiều tài khoản Cloudinary, hệ thống giúp vượt qua giới hạn lưu trữ của gói miễn phí và tối đa hóa hiệu suất truy cập thông qua CDN của Cloudflare.
Tính năng chính
- Hỗ trợ nhiều tài khoản: Phân phối tải lên nhiều tài khoản Cloudinary để tăng dung lượng lưu trữ
- Tối ưu hóa hình ảnh: Tự động tối ưu hóa hình ảnh sau khi tải lên thông qua Jetpack
- Kiểm soát truy cập: Hạn chế số lượng yêu cầu từ mỗi IP và phát hiện hành vi bất thường
- Giao diện thân thiện: Giao diện kéo thả đơn giản, dễ sử dụng
- Hiệu suất cao: Tích hợp với CDN của Cloudflare để tăng tốc độ truy cập
- Upload hàng loạt: Hỗ trợ tải lên nhiều file cùng lúc với cơ chế phân chia batch thông minh
- Quản lý URL đa dạng: Tự động tạo 4 định dạng URL phổ biến cho mỗi ảnh (Direct, Markdown, BBCode, HTML)
Tính năng giao diện người dùng
- Mỗi ảnh tự động tạo 4 định dạng URL: Direct, Markdown, BBCode và HTML
- Click vào URL để tự động copy vào clipboard
- Nút “Copy All” cho phép copy tất cả URL theo định dạng đã chọn
- Dễ dàng chuyển đổi giữa các định dạng URL khác nhau
- Xem trước ảnh trước khi tải lên
- Kiểm tra kích thước và loại file tự động
- Tự động phát hiện và thông báo lỗi
- Giao diện kéo thả trực quan
Đây là phiên bản Image Archive System mình hài lòng nhất, tính ổn định cao, hiệu quả, viết thành 1 bài hướng dẫn cài đặt, vì thú thực nếu không ghi chú lại, sẽ rất dễ quên, nhầm các công đoạn cài đặt
Cài đặt Image Archive System
3 bước đầu tiên là:
- Tạo tài khoản Github nếu chưa có
- Tạo tài khoản Cloudflare mới, để tận dụng 100.000 request miễn phí mỗi ngày
- Tạo các tài khoản Cloudinary để chứa ảnh
- 1 tài khoản Cloudinary mặc định có 25 credit, tối đa lên tới 100 credit, tương đương 100GB dung lượng, quá đủ cho 1 user thông thường sử dụng
- Nếu bạn muốn tạo ra 1 trang tương tự img.bibica.net, public ra cho nhiều người dùng, có thể tạo thêm nhiều tài khoản Cloudinary khác
- Việc tạo nhiều tài khoản Cloudinary bị cấm theo chính sách của hãng, dù thực tế bản thân mình dùng 3-5 tài khoản trong 5 năm nay, vẫn không thấy họ nói gì 😅
Download toàn bộ source code Image Archive System phiên bản storage Cloudinary tại đây
- Tạo 1 dự án mới trên Github, chọn chế độ private
- Giải nén, upload tất cả file vào dự án
Cấu hình server.js
ALLOWED_ORIGINS
: thay bằng domain của bạn (nếu chưa biết được domain là gì, thì sau này cập nhập lại)CLOUDINARY_ACCOUNTS
: thay bằng tài khoản Cloudinary của bạn, muốn dùng thêm tài khoản thì copy thêm thành dòng mới
Mặc định khi vừa tạo tài khoản, login vào, sẽ hiện ra cái thông báo, có sẵn [cloud_name, api_key, api_secret]
["duxl2jwjy", "338626439374432", "xwr_At2WaxAtdK_u04sNpZXKWjM"], ["dga1onmqq", "447836514486728", "2IvUET_Af2o4Aq-7wLyfqh9jnbI"], ["dbvp17k9l", "431535683238757", "I5-J405OgrtKDJKKSx36lwjWaf0"],
RATE_LIMIT
: đang giới hạn 20 lượt upload trong 5 phút (tính theo IP), sửa lại theo nhu cầu của bạnMAX_SIZE_MB
: Cloudinary hỗ trợ tài khoản miễn phí upload tối đa 10MB, đang dùng giá trị cao nhất, sửa lại theo nhu cầu của bạnPATH
: tùy chọn tạo ra url ảnh, mặc định tạo tên file ngẫu nhiên 8 kí tựABUSE_PROTECTION
: đây là hình thức chống các tool, bot request làm tốn tài nguyên database , cấu hình từserver.js
không có giá trị, để như mặc định là đủ
Cấu hình database D1
- Vào Cloudflare, tạo 1 database D1 miễn phí
- Đặt tên:
image-archive-db
(hoặc tên tùy thích)
- Sang tab Console, dán đoạn SQL bên dưới vào, sau đó Execute:
CREATE TABLE IF NOT EXISTS images ( id INTEGER PRIMARY KEY AUTOINCREMENT, folder TEXT, filename TEXT, cloudinary_url TEXT NOT NULL, file_size INTEGER, cloud_name TEXT ); CREATE TABLE IF NOT EXISTS rate_limits ( ip TEXT PRIMARY KEY, count INTEGER, reset_time INTEGER ); CREATE TABLE IF NOT EXISTS abuse_blocks ( ip TEXT PRIMARY KEY, failed_count INTEGER, block_until INTEGER, last_attempt INTEGER ); CREATE INDEX idx_images_folder_filename ON images(folder, filename);
- Lấy database_id (cạnh database_name)
Cấu hình scripts/generate-pages.js
database_name
: thay bằng database_name của bạndatabase_id
: thay bằng database_id của bạnSERVER_PREFIX
: có thể đổi sang 1 tên gọi mà bạn thích, đây là url dùng để gọi các server (mặc định sẽ tạo ra 19 server)
Cấu hình Cloudflare API Tokens
- Tạo 1 API Tokens
- Chọn Template Cloudflare Workers
- Bắt buộc phải chọn vào tài khoản cụ thể (không dùng mặc định tất cả tài khoản)
- Click đơn giản về home, sẽ thấy ACCOUNT_ID
Cấu hình GitHub Actions
- Click vào
Actions
trên dự án, chọnSkip this and set up a workflow yourself
- Điền vào nội dung bên dưới
name: Deploy Cloudflare Pages on: workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest env: CLOUDFLARE_API_TOKEN: xxxxxxxxxxxxxxx CLOUDFLARE_ACCOUNT_ID: xxxxxxxxxxxxx CUSTOM_DOMAIN_SUFFIX: bibica.net SERVER_PREFIX: iserver SERVER_COUNT: 19 steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '20' - name: Install Wrangler run: npm install -g wrangler - name: Generate page configurations run: node scripts/generate-pages.js - name: Deploy Cloudflare Pages run: | for i in $(seq 1 $SERVER_COUNT); do PROJECT_NAME="${SERVER_PREFIX}${i}" CUSTOM_DOMAIN="${PROJECT_NAME}.${CUSTOM_DOMAIN_SUFFIX}" PAGES_DOMAIN="${PROJECT_NAME}.pages.dev" cd "${SERVER_PREFIX}/${PROJECT_NAME}" if ! wrangler pages project list | grep -q "$PROJECT_NAME"; then wrangler pages project create "$PROJECT_NAME" --production-branch=main > /dev/null echo "Successfully created the '${PROJECT_NAME}' project. It will be available at https://${PAGES_DOMAIN} once you create your first deployment." fi wrangler pages deploy . --project-name="$PROJECT_NAME" --commit-dirty=true > /dev/null DOMAIN_EXISTS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/${PROJECT_NAME}/domains" \ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" | grep -o "$CUSTOM_DOMAIN" | head -1) if [ -z "$DOMAIN_EXISTS" ]; then curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/${PROJECT_NAME}/domains" \ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ -H "Content-Type: application/json" \ --data '{"name":"'"$CUSTOM_DOMAIN"'"}' > /dev/null fi echo "CNAME ${CUSTOM_DOMAIN} -> ${PAGES_DOMAIN}" cd ../.. done
CLOUDFLARE_API_TOKEN
: thay bằng token tạo ra bước bước trênCLOUDFLARE_ACCOUNT_ID
: thay bằng ID tài khoản của bạnCUSTOM_DOMAIN_SUFFIX
: thay bibica.net bằng domain của bạnSERVER_PREFIX
: dùng theo tên tạo ra ở bướcscripts/generate-pages.js
Chạy runs workflow
- Actions -> Deploy Cloudflare Pages
- Đây là bước cuối cùng để public các server của bạn ra ngoài
- Lần đầu chạy thường mất 4-8 phút, vì phải tạo, cấu hình 19 server, các url server tạo ra sẽ từ Cloudflare Pages, sẽ có dạng iserver15-5uq.
pages.dev
- Nếu muốn dùng subdomain thì tạo CNAME theo tên hiển thị ra, như thèng img.bibica.net, sẽ dùng server
iserver1
.bibica.net ->iserver19
.bibica.net - Lười thì dùng .
pages.dev
cho nhanh, vì thường user vào upload ảnh cũng chẳng ai quan tâm mấy cái này
Sau này, nếu chỉnh sửa code, bổ xung tài khoản Cloudinary tại server.js
, cần chạy lại runs workflow Deploy Cloudflare Pages, nó sẽ tự cấu hình toàn bộ 19 server
Cấu hình client
Mở file index.html
ra, tìm kiếm dòng this.servers
Cá nhân mình đang dùng URL iserver1
.bibica.net -> iserver19
.bibica.net, điền thế cho gọn, nếu bạn dùng ít server hơn, hoặc dùng các sub .pages.dev
thì dùng trực tiếp URL cho tiện
this.servers = [ "https://iserver1-h9b.pages.dev", "https://iserver2-310.pages.dev", "https://iserver3-72y.pages.dev", "https://iserver4-dhg.pages.dev", "https://iserver5-3oe.pages.dev", "https://iserver6-5qe.pages.dev", "https://iserver7-9u7.pages.dev", "https://iserver8-epr.pages.dev", "https://iserver9-bna.pages.dev", "https://iserver10-9h8.pages.dev", "https://iserver11-8vp.pages.dev", "https://iserver12-f4l.pages.dev", "https://iserver13-ehj.pages.dev", "https://iserver14-f3g.pages.dev", "https://iserver15-5uq.pages.dev", "https://iserver16-87s.pages.dev", "https://iserver17-br0.pages.dev", "https://iserver18-124.pages.dev", "https://iserver19-bfe.pages.dev" ];
Các cấu hình giới hạn khác, maxFiles
, maxFileSize
điền theo cấu hình bạn giới hạn ở server, cho client và server đồng bộ với nhau
Cấu hình quan trọng nhất ở client là this.maxBatchSize = 40 * 1024 * 1024;
, giá trị mình đang duy trì là batch 40MB, khi user upload cùng lúc nhiều file (100-1000) dung lượng nhỏ, sẽ thấy sự tối ưu của tùy chọn này, giá trị này nên duy trì < 40MB, cao hơn không có nhiều tác dụng, và dễ làm Cloudflare báo lỗi thiếu RAM
Sau khi chỉnh sửa lại đường dẫn this.servers
tại index.html thì có thể triển khai lên Cloudflare Pages
Triển khai lên Cloudflare Pages
- Vào Cloudflare Dashboard > Pages > Create a project:
- Kết nối với GitHub repository của bạn
- Cấu hình build: (tất cả để mặc định)
- Nhấn Save and Deploy
Cấu hình Bind D1 Database
Vào Settings > Functions > D1 database bindings:
Variable name
: DBD1 database
: image-archive-db (mặc định nó sẽ hiện ra db bạn đã tạo)
- Quay lại tab Deployments > Production > View details
- Tại Deployment details, nhấn Retry deployment
Nếu sử dụng domains: demo-7et.pages.dev
để chạy Image Archive System thì cần sửa lại ALLOWED_ORIGINS
theo domain này tại server.js
Mỗi khi sửa server.js, cần chạy lại runs workflow Deploy Cloudflare Pages để cập nhập lại
Bản Image Archive System viết chạy trên nhiều server upload, nên quá trình cập nhập server hơi phức tạp, sử dụng thực tế chỉ có lần đầu tiên làm hơi nhiều thao tác, sử dụng thực tế nếu bạn dùng khoảng 10 tài khoản Cloudinary thì chắc 10 năm không cần nhìn lại luôn, vì khó ai upload ảnh tới 250GB lắm
Trong miss/cron-worker.js
, mình có tạo sẵn 1 cron chạy qua woker, nếu tài khoản dùng > 15GB dung lượng sẽ, gửi thông báo qua Telegram, bạn nào thích có thể cài thêm, tránh tài khoản dùng quá credit
Kết luận
Image Archive System dùng cá nhân, mình không thấy quá nhiều ưu điểm, nó hợp hơn cho bạn nào làm mấy trang dạng truyện coi online, upload cường độ cao, URL ảnh được ẩn, gần như không ai biết được ảnh để ở đâu, băng thông thì Cloudflare miễn phí
Chính sách bình luận: Chúng tôi rất trân trọng các bình luận của bạn và cảm ơn thời gian bạn dành để chia sẻ ý tưởng và phản hồi.
Ghi chú: Những bình luận được xác định là spam hoặc chỉ mang tính quảng cáo sẽ bị xóa.
• Để cải thiện trải nghiệm bình luận, chúng tôi khuyến khích bạn tạo một tài khoản Gravatar. Thêm avatar vào tài khoản Gravatar sẽ giúp bình luận của bạn dễ nhận diện hơn đối với các thành viên khác.
• ✂️ Sao chép và 📋 Dán Emoji 💪 giúp bình luận thêm sinh động và thú vị!