Vì vậy, bạn đang điền vào một biểu mẫu trực tuyến và nó yêu cầu bạn tải lên một tệp. Bạn nhấp vào đầu vào, chọn một tệp từ máy tính để bàn của mình và mọi thứ đã ổn. Nhưng có điều gì đó xảy ra. Mạng bị ngắt, tệp biến mất và bạn phải tải lại tệp. Kết nối mạng kém có thể khiến bạn mất nhiều thời gian vô lý để cố gắng tải tệp lên thành công.
Điều làm hỏng trải nghiệm của người dùng bắt nguồn từ việc phải liên tục kiểm tra tính ổn định của mạng và thử tải lên nhiều lần. Mặc dù chúng ta không thể làm được nhiều về kết nối mạng, nhưng với tư cách là nhà phát triển, chúng ta luôn có thể làm gì đó để giảm bớt nỗi đau đi kèm với vấn đề này.
Một trong những cách chúng ta có thể giải quyết vấn đề này là điều chỉnh hệ thống tải ảnh lên theo cách cho phép người dùng tải ảnh lên ngoại tuyến — loại bỏ nhu cầu về kết nối mạng đáng tin cậy, sau đó hệ thống sẽ thử lại quy trình tải lên khi mạng ổn định mà không cần người dùng can thiệp.
Bài viết này sẽ tập trung vào việc giải thích cách xây dựng hệ thống tải ảnh thân thiện với ngoại tuyến bằng các công nghệ PWA (ứng dụng web tiến bộ) như

Như thể hiện trong sơ đồ luồng, quy trình diễn ra như sau:
Để sử dụng nhân viên dịch vụ, trước tiên bạn phải đăng ký một nhân viên:
Lưu ý: Tôi chỉ sử dụng thuộc tính
Sau khi đăng ký sự kiện đồng bộ, bạn cần lắng nghe sự kiện đó trong service worker.
Hàm
Với hình ảnh đã lưu trữ và đồng bộ hóa nền được thiết lập, hệ thống đã sẵn sàng tải hình ảnh lên bất cứ khi nào kết nối mạng được khôi phục.
Như vậy là toàn bộ quá trình đã hoàn tất!
Điều làm hỏng trải nghiệm của người dùng bắt nguồn từ việc phải liên tục kiểm tra tính ổn định của mạng và thử tải lên nhiều lần. Mặc dù chúng ta không thể làm được nhiều về kết nối mạng, nhưng với tư cách là nhà phát triển, chúng ta luôn có thể làm gì đó để giảm bớt nỗi đau đi kèm với vấn đề này.
Một trong những cách chúng ta có thể giải quyết vấn đề này là điều chỉnh hệ thống tải ảnh lên theo cách cho phép người dùng tải ảnh lên ngoại tuyến — loại bỏ nhu cầu về kết nối mạng đáng tin cậy, sau đó hệ thống sẽ thử lại quy trình tải lên khi mạng ổn định mà không cần người dùng can thiệp.
Bài viết này sẽ tập trung vào việc giải thích cách xây dựng hệ thống tải ảnh thân thiện với ngoại tuyến bằng các công nghệ PWA (ứng dụng web tiến bộ) như
IndexedDB
, trình xử lý dịch vụ và API đồng bộ hóa nền. Chúng tôi cũng sẽ tóm tắt các mẹo để cải thiện trải nghiệm người dùng cho hệ thống này.Lên kế hoạch cho hệ thống tải ảnh ngoại tuyến
Sau đây là sơ đồ luồng cho hệ thống tải ảnh thân thiện với ngoại tuyến.
Như thể hiện trong sơ đồ luồng, quy trình diễn ra như sau:
- Người dùng chọn image.
Quá trình bắt đầu bằng cách cho phép người dùng chọn hình ảnh của họ. - Hình ảnh được lưu trữ cục bộ trong
IndexedDB
.
Tiếp theo, hệ thống kiểm tra kết nối mạng. Nếu có kết nối mạng, hệ thống sẽ tải hình ảnh trực tiếp lên, tránh sử dụng bộ nhớ cục bộ không cần thiết. Tuy nhiên, nếu mạng không khả dụng, hình ảnh sẽ được lưu trữ trongIndexedDB
. - Service worker phát hiện khi mạng được khôi phục.
Với hình ảnh được lưu trữ trongIndexedDB
, hệ thống sẽ chờ phát hiện khi kết nối mạng được khôi phục để tiếp tục bước tiếp theo. - Các quy trình đồng bộ hóa nền đang chờ tải lên.
Ngay khi kết nối được khôi phục, hệ thống sẽ thử tải hình ảnh lên lại. - Tệp đã được tải lên thành công.
Ngay khi hình ảnh được tải lên, hệ thống sẽ xóa bản sao cục bộ được lưu trữ trongIndexedDB
.
Triển khai hệ thống
Bước đầu tiên trong quá trình triển khai hệ thống là cho phép người dùng chọn hình ảnh của họ. Có nhiều cách khác nhau để bạn có thể thực hiện điều này:- Bạn có thể sử dụng một phần tử
đơn giản;
- Giao diện kéo và thả.
. Có cả hai tùy chọn sẽ giúp cải thiện trải nghiệm của người dùng. Bạn cũng có thể cân nhắc cho phép người dùng dán hình ảnh trực tiếp vào trình duyệt bằng API Clipboard.Đăng ký Service Worker
Trọng tâm của giải pháp này là service worker. Nhân viên dịch vụ của chúng tôi sẽ chịu trách nhiệm lấy hình ảnh từ kho lưu trữIndexedDB
, tải hình ảnh lên khi kết nối internet được khôi phục và xóa kho lưu trữ IndexedDB
khi hình ảnh đã được tải lên.Để sử dụng nhân viên dịch vụ, trước tiên bạn phải đăng ký một nhân viên:
Mã:
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(reg => console.log('Service Worker registered', reg)) .catch(err => console.error('Service Worker registration failed', err));
}
Kiểm tra kết nối mạng
Hãy nhớ rằng, vấn đề chúng ta đang cố gắng giải quyết là do kết nối mạng không đáng tin cậy. Nếu vấn đề này không tồn tại, thì việc cố gắng giải quyết bất cứ điều gì cũng vô nghĩa. Do đó, sau khi chọn hình ảnh, chúng ta cần kiểm tra xem người dùng có kết nối internet đáng tin cậy hay không trước khi đăng ký sự kiện đồng bộ hóa và lưu trữ hình ảnh trongIndexedDB
.
Mã:
function uploadImage() { if (navigator.onLine) { // Upload Image } else { // register Sync Event // Store Images in IndexedDB }
}
navigator.onLine
tại đây để chứng minh cách hệ thống sẽ hoạt động. Thuộc tính navigator.onLine
không đáng tin cậy và tôi khuyên bạn nên đưa ra giải pháp tùy chỉnh để kiểm tra xem người dùng có kết nối với internet hay không. Một cách bạn có thể thực hiện là gửi yêu cầu ping đến điểm cuối máy chủ mà bạn đã tạo.Đăng ký sự kiện đồng bộ
Sau khi thử nghiệm mạng không thành công, bước tiếp theo là đăng ký sự kiện đồng bộ. Sự kiện đồng bộ cần được đăng ký tại thời điểm hệ thống không tải được hình ảnh lên do kết nối internet kém.
Mã:
async function registerSyncEvent() { if ('SyncManager' in window) { const registration = await navigator.serviceWorker.ready; await registration.sync.register('uploadImages'); console.log('Background Sync registered'); }
}
Mã:
self.addEventListener('sync', (event) => { if (event.tag === 'uploadImages') { event.waitUntil(sendImages()); }
});
sendImages
sẽ là một quy trình không đồng bộ sẽ truy xuất hình ảnh từ IndexedDB
và tải lên máy chủ. Đây là giao diện sẽ như sau:
Mã:
async function sendImages() { try { // await image retrieval and upload } catch (error) { // throw error }
}
Mở Cơ sở dữ liệu
Điều đầu tiên chúng ta cần làm để lưu trữ hình ảnh cục bộ là mở một cửa hàngIndexedDB
. Như bạn có thể thấy từ đoạn mã bên dưới, chúng ta đang tạo một biến toàn cục để lưu trữ phiên bản cơ sở dữ liệu. Lý do để làm như vậy là, sau đó, khi chúng ta muốn lấy lại hình ảnh của mình từ IndexedDB
, chúng ta không cần phải viết mã để mở lại cơ sở dữ liệu.
Mã:
let database; // Global variable to store the database instance
function openDatabase() { return new Promise((resolve, reject) => { if (database) return resolve(database); // Return existing database instance const request = indexedDB.open("myDatabase", 1); request.onerror = (event) => { console.error("Database error:", event.target.error); reject(event.target.error); // Reject the promise on error }; request.onupgradeneeded = (event) => { const db = event.target.result; // Create the "images" object store if it doesn't exist. if (!db.objectStoreNames.contains("images")) { db.createObjectStore("images", { keyPath: "id" }); } console.log("Database setup complete."); }; request.onsuccess = (event) => { database = event.target.result; // Store the database instance globally resolve(database); // Resolve the promise with the database instance }; });
}
Lưu trữ hình ảnh trong IndexedDB
Với cửa hàngIndexedDB
mở, giờ đây chúng ta có thể lưu trữ hình ảnh của mình.Sau đây là cách bạn có thể lưu trữ hình ảnh trongBây giờ, bạn có thể tự hỏi tại sao một giải pháp dễ dàng hơn nhưlocalStorage
lại không được sử dụng cho mục đích này.
Lý do cho điều đó làIndexedDB
hoạt động không đồng bộ và không chặn luồng JavaScript chính, trong khilocalStorage
chạy đồng bộ và có thể chặn luồng JavaScript chính nếu luồng này đang được sử dụng.
IndexedDB
:
Mã:
async function storeImages(file) { // Open the IndexedDB database. const db = await openDatabase(); // Create a transaction with read and write access. const transaction = db.transaction("images", "readwrite"); // Access the "images" object store. const store = transaction.objectStore("images"); // Define the image record to be stored. const imageRecord = { id: IMAGE_ID, // a unique ID image: file // Store the image file (Blob) }; // Add the image record to the store. const addRequest = store.add(imageRecord); // Handle successful addition. addRequest.onsuccess = () => console.log("Image added successfully!"); // Handle errors during insertion. addRequest.onerror = (e) => console.error("Error storing image:", e.target.error);
}
Truy xuất và tải hình ảnh lên
Sau khi kết nối mạng được khôi phục, sự kiện đồng bộ sẽ kích hoạt và trình xử lý dịch vụ sẽ lấy hình ảnh từIndexedDB
và tải lên.
Mã:
async function retrieveAndUploadImage(IMAGE_ID) { try { const db = await openDatabase(); // Ensure the database is open const transaction = db.transaction("images", "readonly"); const store = transaction.objectStore("images"); const request = store.get(IMAGE_ID); request.onsuccess = function (event) { const image = event.target.result; if (image) { // upload Image to server here } else { console.log("No image found with ID:", IMAGE_ID); } }; request.onerror = () => { console.error("Error retrieving image."); }; } catch (error) { console.error("Failed to open database:", error); }
}
Xóa IndexedDB Cơ sở dữ liệu
Sau khi hình ảnh được tải lên, kho lưu trữIndexedDB
không còn cần thiết nữa. Do đó, bạn nên xóa nó cùng với nội dung của nó để giải phóng dung lượng lưu trữ.
Mã:
function deleteDatabase() { // Check if there's an open connection to the database. if (database) { database.close(); // Close the database connection console.log("Database connection closed."); } // Request to delete the database named "myDatabase". const deleteRequest = indexedDB.deleteDatabase("myDatabase"); // Handle successful deletion of the database. deleteRequest.onsuccess = function () { console.log("Database deleted successfully!"); }; // Handle errors that occur during the deletion process. deleteRequest.onerror = function (event) { console.error("Error deleting database:", event.target.error); }; // Handle cases where the deletion is blocked (e.g., if there are still open connections). deleteRequest.onblocked = function () { console.warn("Database deletion blocked. Close open connections and try again."); };
}
Những cân nhắc và hạn chế
Mặc dù chúng tôi đã nỗ lực rất nhiều để giúp cải thiện trải nghiệm bằng cách hỗ trợ tải lên ngoại tuyến, nhưng hệ thống vẫn có những hạn chế. Tôi nghĩ mình sẽ nêu cụ thể những điều đó vì bạn nên biết giải pháp này có thể không đáp ứng được nhu cầu của bạn ở đâu.- Không có khả năng phát hiện kết nối Internet đáng tin cậy
JavaScript không cung cấp cách hoàn hảo để phát hiện trạng thái trực tuyến. Vì lý do này, bạn cần đưa ra giải pháp tùy chỉnh để phát hiện trạng thái trực tuyến. - Giải pháp chỉ dành cho Chromium
API đồng bộ hóa nền hiện chỉ giới hạn ở các trình duyệt dựa trên Chromium. Do đó, giải pháp này chỉ được hỗ trợ bởi các trình duyệt Chromium. Điều đó có nghĩa là bạn sẽ cần một giải pháp mạnh mẽ hơn nếu phần lớn người dùng của bạn sử dụng trình duyệt không phải Chromium. -
IndexedDB
Chính sách lưu trữ
Trình duyệt áp đặt giới hạn lưu trữ và chính sách trục xuất đối vớiIndexedDB
. Ví dụ, trong Safari, dữ liệu được lưu trữ trongIndexedDB
có tuổi thọ là bảy ngày nếu người dùng không tương tác với trang web. Đây là điều bạn nên ghi nhớ nếu bạn đưa ra một giải pháp thay thế cho API đồng bộ hóa nền hỗ trợ Safari.
Nâng cao trải nghiệm người dùng
Vì toàn bộ quá trình diễn ra ở chế độ nền, chúng ta cần một cách để thông báo cho người dùng khi hình ảnh được lưu trữ, chờ tải lên hoặc đã tải lên thành công. Việc triển khai một số thành phần UI nhất định cho mục đích này thực sự sẽ nâng cao trải nghiệm cho người dùng. Các thành phần UI này có thể bao gồm thông báo toast, chỉ báo trạng thái tải lên như spinner (để hiển thị các quy trình đang hoạt động), thanh tiến trình (để hiển thị tiến trình trạng thái), chỉ báo trạng thái mạng hoặc các nút để cung cấp tùy chọn thử lại và hủy.Kết thúc
Kết nối internet kém có thể làm gián đoạn trải nghiệm của người dùng đối với ứng dụng web. Tuy nhiên, bằng cách tận dụng các công nghệ PWA nhưIndexedDB
, service worker và Background Sync API, các nhà phát triển có thể giúp cải thiện độ tin cậy của các ứng dụng web cho người dùng của họ, đặc biệt là những người ở những khu vực có kết nối internet không ổn định.