构建离线友好的图片上传系统
网络连接不稳定不意味着用户体验必须糟糕。借助PWA技术如IndexedDB、Service Worker和Background Sync API,你可以构建一个离线友好的图片上传系统,它能排队上传并在网络恢复时自动重试 - 让用户即使离线也能无忧上传。
问题背景
当你填写在线表单需要上传文件时,网络突然中断,文件消失,不得不重新上传。糟糕的网络连接会让你花费大量时间反复尝试上传。这种需要不断检查网络状态并多次重试的体验非常糟糕。
解决方案概述
我们可以通过调整图片上传系统,让用户能够离线上传图片 - 消除对可靠网络连接的依赖,并在网络恢复时自动重试上传过程,无需用户干预。
系统流程图
- 用户选择图片
- 图片存储在本地IndexedDB中
- Service Worker检测网络恢复
- 后台同步处理待上传内容
- 文件成功上传后删除本地副本
系统实现
选择图片方式
提供两种选择方式:
- 简单的
<input type="file">
元素
- 拖放界面
建议同时提供两种方式以提升用户体验,还可以考虑使用Clipboard API允许用户直接粘贴图片。
注册Service Worker
1
2
3
4
5
|
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));
}
|
检查网络连接
1
2
3
4
5
6
7
8
|
function uploadImage() {
if (navigator.onLine) {
// 直接上传图片
} else {
// 注册同步事件
// 将图片存储在IndexedDB中
}
}
|
注意:navigator.onLine
属性并不可靠,建议实现自定义的网络检测方案。
注册同步事件
1
2
3
4
5
6
7
|
async function registerSyncEvent() {
if ('SyncManager' in window) {
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('uploadImages');
console.log('Background Sync registered');
}
}
|
Service Worker监听同步事件
1
2
3
4
5
|
self.addEventListener('sync', (event) => {
if (event.tag === 'uploadImages') {
event.waitUntil(sendImages());
}
});
|
打开IndexedDB数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
let database; // 全局变量存储数据库实例
function openDatabase() {
return new Promise((resolve, reject) => {
if (database) return resolve(database);
const request = indexedDB.open("myDatabase", 1);
request.onerror = (event) => {
console.error("Database error:", event.target.error);
reject(event.target.error);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains("images")) {
db.createObjectStore("images", { keyPath: "id" });
}
console.log("Database setup complete.");
};
request.onsuccess = (event) => {
database = event.target.result;
resolve(database);
};
});
}
|
在IndexedDB中存储图片
1
2
3
4
5
6
7
8
9
10
11
12
|
async function storeImages(file) {
const db = await openDatabase();
const transaction = db.transaction("images", "readwrite");
const store = transaction.objectStore("images");
const imageRecord = {
id: IMAGE_ID, // 唯一ID
image: file // 存储图片文件(Blob)
};
const addRequest = store.add(imageRecord);
addRequest.onsuccess = () => console.log("图片添加成功!");
addRequest.onerror = (e) => console.error("存储图片错误:", e.target.error);
}
|
检索并上传图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
async function retrieveAndUploadImage(IMAGE_ID) {
try {
const db = await openDatabase();
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) {
// 在此上传图片到服务器
} else {
console.log("未找到ID对应的图片:", IMAGE_ID);
}
};
request.onerror = () => {
console.error("检索图片错误。");
};
} catch (error) {
console.error("打开数据库失败:", error);
}
}
|
删除IndexedDB数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
function deleteDatabase() {
if (database) {
database.close();
console.log("数据库连接已关闭。");
}
const deleteRequest = indexedDB.deleteDatabase("myDatabase");
deleteRequest.onsuccess = function () {
console.log("数据库删除成功!");
};
deleteRequest.onerror = function (event) {
console.error("删除数据库错误:", event.target.error);
};
deleteRequest.onblocked = function () {
console.warn("数据库删除被阻止。请关闭所有连接后重试。");
};
}
|
限制与注意事项
- 没有可靠的网络连接检测:JavaScript没有提供万无一失的在线状态检测方法。
- 仅限Chromium解决方案:Background Sync API目前仅限于基于Chromium的浏览器。
- IndexedDB存储策略:浏览器对IndexedDB有存储限制和回收策略。
提升用户体验
由于整个过程在后台进行,我们需要通过UI元素告知用户图片的状态:
- Toast通知
- 上传状态指示器(如旋转图标)
- 进度条
- 网络状态指示器
- 重试和取消按钮
总结
通过利用PWA技术如IndexedDB、Service Worker和Background Sync API,开发者可以提升Web应用在不可靠网络环境下的可靠性,特别为网络连接不稳定的地区用户提供更好的体验。
(评论部分略)