浏览器API完全指南:FileReader、Canvas、Web Workers深度解析
现代浏览器提供了丰富的API,让Web应用拥有接近原生应用的能力。从文件处理到多线程计算,从本地存储到剪贴板操作,这些API为开发者打开了无限可能。
目录
File API文件处理
FileReader基础
// FileReader用于读取文件内容 class FileProcessor { // 读取为文本 static readAsText(file, encoding = 'UTF-8') { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => resolve(e.target.result); reader.onerror = reject; reader.readAsText(file, encoding); }); } // 读取为DataURL(Base64) static readAsDataURL(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => resolve(e.target.result); reader.onerror = reject; reader.readAsDataURL(file); }); } // 读取为ArrayBuffer(二进制) static readAsArrayBuffer(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => resolve(e.target.result); reader.onerror = reject; reader.readAsArrayBuffer(file); }); } } // 使用 const file = document.querySelector('#fileInput').files[0]; // 读取文本文件 const text = await FileProcessor.readAsText(file); console.log(text); // 读取图片为Base64 const dataUrl = await FileProcessor.readAsDataURL(file); document.querySelector('img').src = dataUrl;
文件上传进度监控
function uploadWithProgress(file, url, onProgress) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); const formData = new FormData(); formData.append('file', file); // 监听上传进度 xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; onProgress(percentComplete); } }); xhr.addEventListener('load', () => { if (xhr.status === 200) { resolve(JSON.parse(xhr.responseText)); } else { reject(new Error(`上传失败: ${xhr.status}`)); } }); xhr.addEventListener('error', reject); xhr.open('POST', url); xhr.send(formData); }); } // 使用 uploadWithProgress(file, '/api/upload', (progress) => { console.log(`上传进度: ${progress.toFixed(2)}%`); progressBar.style.width = `${progress}%`; });
大文件分片上传
class ChunkedUploader { constructor(file, chunkSize = 1024 * 1024) { this.file = file; this.chunkSize = chunkSize; this.chunks = Math.ceil(file.size / chunkSize); } async upload(url) { for (let i = 0; i < this.chunks; i++) { const start = i * this.chunkSize; const end = Math.min(start + this.chunkSize, this.file.size); const chunk = this.file.slice(start, end); await this.uploadChunk(chunk, i, url); console.log(`上传进度: ${(((i + 1) / this.chunks) * 100).toFixed(2)}%`); } // 通知服务器合并文件 await this.mergeChunks(url); } async uploadChunk(chunk, index, url) { const formData = new FormData(); formData.append('chunk', chunk); formData.append('index', index); formData.append('filename', this.file.name); formData.append('totalChunks', this.chunks); const response = await fetch(url, { method: 'POST', body: formData, }); if (!response.ok) { throw new Error(`分片${index}上传失败`); } } async mergeChunks(url) { const response = await fetch(`${url}/merge`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename: this.file.name, totalChunks: this.chunks, }), }); return response.json(); } } // 使用 const uploader = new ChunkedUploader(largeFile, 1024 * 1024); // 1MB分片 await uploader.upload('/api/upload');
Blob对象操作
// 创建Blob const blob = new Blob(['Hello, World!'], { type: 'text/plain' }); // Blob转文本 async function blobToText(blob) { return await blob.text(); } // Blob转ArrayBuffer async function blobToArrayBuffer(blob) { return await blob.arrayBuffer(); } // 下载Blob function downloadBlob(blob, filename) { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); } // ArrayBuffer转Blob function arrayBufferToBlob(buffer, mimeType) { return new Blob([buffer], { type: mimeType }); } // 合并多个Blob function mergeBlobs(blobs, mimeType) { return new Blob(blobs, { type: mimeType }); }
Canvas绘图API
Canvas基础
class CanvasDrawing { constructor(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); } // 绘制矩形 drawRect(x, y, width, height, color) { this.ctx.fillStyle = color; this.ctx.fillRect(x, y, width, height); } // 绘制圆形 drawCircle(x, y, radius, color) { this.ctx.fillStyle = color; this.ctx.beginPath(); this.ctx.arc(x, y, radius, 0, Math.PI * 2); this.ctx.fill(); } // 绘制文字 drawText(text, x, y, options = {}) { const { fontSize = 16, fontFamily = 'Arial', color = 'black', align = 'left' } = options; this.ctx.font = `${fontSize}px ${fontFamily}`; this.ctx.fillStyle = color; this.ctx.textAlign = align; this.ctx.fillText(text, x, y); } // 绘制线条 drawLine(x1, y1, x2, y2, color = 'black', width = 1) { this.ctx.strokeStyle = color; this.ctx.lineWidth = width; this.ctx.beginPath(); this.ctx.moveTo(x1, y1); this.ctx.lineTo(x2, y2); this.ctx.stroke(); } // 清空画布 clear() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } } // 使用 const canvas = document.querySelector('#myCanvas'); const drawing = new CanvasDrawing(canvas); drawing.drawRect(50, 50, 100, 100, 'blue'); drawing.drawCircle(200, 100, 50, 'red'); drawing.drawText('Hello Canvas!', 100, 200, { fontSize: 24 });
图片处理
class ImageProcessor { constructor(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); } // 加载图片 async loadImage(src) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); } // 绘制图片 drawImage(img, x = 0, y = 0, width, height) { if (width && height) { this.ctx.drawImage(img, x, y, width, height); } else { this.ctx.drawImage(img, x, y); } } // 获取像素数据 getImageData(x = 0, y = 0, width, height) { width = width || this.canvas.width; height = height || this.canvas.height; return this.ctx.getImageData(x, y, width, height); } // 修改像素数据 putImageData(imageData, x = 0, y = 0) { this.ctx.putImageData(imageData, x, y); } // 灰度滤镜 grayscale() { const imageData = this.getImageData(); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const avg = (data[i] + data[i + 1] + data[i + 2]) / 3; data[i] = avg; // Red data[i + 1] = avg; // Green data[i + 2] = avg; // Blue } this.putImageData(imageData); } // 反色滤镜 invert() { const imageData = this.getImageData(); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { data[i] = 255 - data[i]; // Red data[i + 1] = 255 - data[i + 1]; // Green data[i + 2] = 255 - data[i + 2]; // Blue } this.putImageData(imageData); } // 亮度调整 brightness(value) { const imageData = this.getImageData(); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { data[i] += value; // Red data[i + 1] += value; // Green data[i + 2] += value; // Blue } this.putImageData(imageData); } // 导出为Blob async toBlob(type = 'image/png', quality = 0.92) { return new Promise((resolve) => { this.canvas.toBlob(resolve, type, quality); }); } } // 使用 const processor = new ImageProcessor(canvas); const img = await processor.loadImage('photo.jpg'); processor.drawImage(img, 0, 0, canvas.width, canvas.height); processor.grayscale(); // 应用灰度滤镜 const blob = await processor.toBlob('image/jpeg', 0.8);
Canvas动画
class CanvasAnimation { constructor(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); this.animationId = null; this.isRunning = false; } start(callback) { this.isRunning = true; const animate = (timestamp) => { if (!this.isRunning) return; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); callback(timestamp); this.animationId = requestAnimationFrame(animate); }; this.animationId = requestAnimationFrame(animate); } stop() { this.isRunning = false; if (this.animationId) { cancelAnimationFrame(this.animationId); } } } // 使用:弹跳球动画 const animation = new CanvasAnimation(canvas); let x = 50; let y = 50; let vx = 2; let vy = 3; const radius = 20; animation.start(() => { // 绘制球 ctx.fillStyle = 'red'; ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); // 更新位置 x += vx; y += vy; // 碰撞检测 if (x + radius > canvas.width || x - radius < 0) { vx = -vx; } if (y + radius > canvas.height || y - radius < 0) { vy = -vy; } });
Web Workers多线程
基础使用
// main.js const worker = new Worker('worker.js'); // 发送消息给Worker worker.postMessage({ type: 'start', data: [1, 2, 3, 4, 5] }); // 接收Worker的消息 worker.onmessage = (e) => { console.log('Worker返回:', e.data); }; worker.onerror = (error) => { console.error('Worker错误:', error); }; // 终止Worker // worker.terminate();
// worker.js self.onmessage = (e) => { const { type, data } = e.data; if (type === 'start') { // 执行耗时计算 const result = heavyCalculation(data); // 返回结果 self.postMessage({ type: 'result', data: result }); } }; function heavyCalculation(data) { // 模拟耗时计算 let sum = 0; for (let i = 0; i < 1000000000; i++) { sum += Math.sqrt(i); } return sum; }
实用案例:大数据排序
// main.js class WorkerPool { constructor(workerScript, size = 4) { this.workers = Array(size) .fill(null) .map(() => new Worker(workerScript)); this.queue = []; this.activeWorkers = new Set(); } async execute(data) { return new Promise((resolve, reject) => { const task = { data, resolve, reject }; const worker = this.getAvailableWorker(); if (worker) { this.runTask(worker, task); } else { this.queue.push(task); } }); } getAvailableWorker() { return this.workers.find((w) => !this.activeWorkers.has(w)); } runTask(worker, task) { this.activeWorkers.add(worker); worker.onmessage = (e) => { task.resolve(e.data); this.activeWorkers.delete(worker); // 处理队列中的下一个任务 if (this.queue.length > 0) { const nextTask = this.queue.shift(); this.runTask(worker, nextTask); } }; worker.onerror = (error) => { task.reject(error); this.activeWorkers.delete(worker); }; worker.postMessage(task.data); } terminate() { this.workers.forEach((w) => w.terminate()); } } // 使用 const pool = new WorkerPool('sort-worker.js', 4); const arrays = [ [5, 2, 8, 1, 9], [3, 7, 4, 6, 0], // ... 更多数组 ]; const results = await Promise.all(arrays.map((arr) => pool.execute(arr))); console.log('所有排序完成:', results);
// sort-worker.js self.onmessage = (e) => { const data = e.data; const sorted = data.sort((a, b) => a - b); self.postMessage(sorted); };
Shared Worker(共享Worker)
// shared-worker.js const connections = []; self.onconnect = (e) => { const port = e.ports[0]; connections.push(port); port.onmessage = (event) => { // 广播消息给所有连接 connections.forEach((p) => { p.postMessage(event.data); }); }; port.start(); };
// page1.js const worker = new SharedWorker('shared-worker.js'); worker.port.onmessage = (e) => { console.log('收到消息:', e.data); }; worker.port.postMessage('来自页面1的消息');
IndexedDB本地存储
基础操作
class IndexedDBManager { constructor(dbName, version = 1) { this.dbName = dbName; this.version = version; this.db = null; } async open(stores) { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, this.version); request.onerror = () => reject(request.error); request.onsuccess = () => { this.db = request.result; resolve(this.db); }; request.onupgradeneeded = (e) => { const db = e.target.result; stores.forEach(({ name, options }) => { if (!db.objectStoreNames.contains(name)) { db.createObjectStore(name, options); } }); }; }); } async add(storeName, data) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.add(data); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async get(storeName, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const request = store.get(key); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async getAll(storeName) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const request = store.getAll(); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async update(storeName, data) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.put(data); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async delete(storeName, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.delete(key); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } } // 使用 const db = new IndexedDBManager('MyDatabase'); await db.open([ { name: 'users', options: { keyPath: 'id', autoIncrement: true }, }, { name: 'posts', options: { keyPath: 'id', autoIncrement: true }, }, ]); // 添加数据 await db.add('users', { name: 'Alice', email: 'alice@example.com' }); // 获取数据 const user = await db.get('users', 1); // 获取所有数据 const allUsers = await db.getAll('users');
索引查询
class IndexedDBWithIndex extends IndexedDBManager { async createIndex(storeName, indexName, keyPath, options = {}) { // 需要在onupgradeneeded中创建索引 // 这里展示如何使用已存在的索引查询 } async getByIndex(storeName, indexName, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const index = store.index(indexName); const request = index.get(key); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async getAllByIndex(storeName, indexName, query) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], 'readonly'); const store = transaction.objectStore(storeName); const index = store.index(indexName); const request = index.getAll(query); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } } // 创建数据库时添加索引 const request = indexedDB.open('MyDatabase', 2); request.onupgradeneeded = (e) => { const db = e.target.result; if (!db.objectStoreNames.contains('users')) { const store = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true }); // 创建索引 store.createIndex('email', 'email', { unique: true }); store.createIndex('name', 'name', { unique: false }); } }; // 通过email索引查询 const user = await db.getByIndex('users', 'email', 'alice@example.com');
Clipboard API剪贴板
读写剪贴板
class ClipboardManager { // 复制文本到剪贴板 static async copyText(text) { try { await navigator.clipboard.writeText(text); return true; } catch (error) { console.error('复制失败:', error); return false; } } // 读取剪贴板文本 static async pasteText() { try { const text = await navigator.clipboard.readText(); return text; } catch (error) { console.error('粘贴失败:', error); return null; } } // 复制图片到剪贴板 static async copyImage(blob) { try { await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]); return true; } catch (error) { console.error('复制图片失败:', error); return false; } } // 读取剪贴板图片 static async pasteImage() { try { const items = await navigator.clipboard.read(); for (const item of items) { for (const type of item.types) { if (type.startsWith('image/')) { const blob = await item.getType(type); return blob; } } } return null; } catch (error) { console.error('粘贴图片失败:', error); return null; } } } // 使用 // 复制文本 await ClipboardManager.copyText('Hello, Clipboard!'); // 粘贴文本 const text = await ClipboardManager.pasteText(); // 复制图片 const imageBlob = await fetch('image.jpg').then((r) => r.blob()); await ClipboardManager.copyImage(imageBlob); // 粘贴图片 const pastedImage = await ClipboardManager.pasteImage(); if (pastedImage) { const url = URL.createObjectURL(pastedImage); document.querySelector('img').src = url; }
其他实用API
Geolocation API(地理位置)
class GeolocationHelper { static async getCurrentPosition() { return new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition( (position) => { resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy: position.coords.accuracy, }); }, reject, { enableHighAccuracy: true, timeout: 5000 } ); }); } static watchPosition(callback) { return navigator.geolocation.watchPosition( (position) => { callback({ latitude: position.coords.latitude, longitude: position.coords.longitude, }); }, console.error, { enableHighAccuracy: true } ); } } // 使用 const position = await GeolocationHelper.getCurrentPosition(); console.log(`位置: ${position.latitude}, ${position.longitude}`);
Notification API(通知)
class NotificationHelper { static async requestPermission() { if (!('Notification' in window)) { throw new Error('浏览器不支持通知'); } const permission = await Notification.requestPermission(); return permission === 'granted'; } static show(title, options = {}) { if (Notification.permission === 'granted') { return new Notification(title, options); } } } // 使用 const granted = await NotificationHelper.requestPermission(); if (granted) { NotificationHelper.show('新消息', { body: '您有一条新消息', icon: '/icon.png', tag: 'message-1', }); }
Battery API(电池状态)
async function getBatteryInfo() { if (!('getBattery' in navigator)) { console.log('不支持Battery API'); return; } const battery = await navigator.getBattery(); console.log(`电量: ${battery.level * 100}%`); console.log(`充电中: ${battery.charging}`); console.log(`充电时间: ${battery.chargingTime}秒`); console.log(`放电时间: ${battery.dischargingTime}秒`); // 监听电量变化 battery.addEventListener('levelchange', () => { console.log(`电量变化: ${battery.level * 100}%`); }); }
总结
现代浏览器API为Web应用提供了强大的能力:
核心API
- File API - 文件处理
- Canvas - 图形绘制和图像处理
- Web Workers - 多线程计算
- IndexedDB - 大容量本地存储
- Clipboard - 剪贴板操作
最佳实践
- ✅ 使用Web Workers处理耗时计算
- ✅ IndexedDB存储大量数据
- ✅ Canvas处理图片避免服务端
- ✅ 检查API兼容性
- ✅ 优雅降级处理
相关工具
- 📁 文件处理工具 - 多种文件格式转换
- 🎨 Canvas图片编辑器 - 在线图片编辑
- 📋 剪贴板工具 - 剪贴板增强
关键词: 浏览器API, File API, Canvas, Web Workers, IndexedDB, Clipboard API
更新时间: 2026-01-05