IndexedDB storeDrawing对象时发生catch报 DOMException: Failed to execute 'put' on 'IDBObjectStore': HTMLCanvasElement object could not be cloned. at eval (webpack-internal:///./src/utils/indexedDB.js:44:27) at new Promise (<anonymous>) at storeDrawing (webpack-internal:///./src/utils/indexedDB.js:34:10) at eval (webpack-internal:///./node_modules/babel-loader/lib/index.js!./node_modules/ts-loader/index.js??clonedRuleSet-41.use[1]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/components/MxShow.vue?vue&type=script&lang=ts:680:89) 是什么原因
你遇到的错误提示:DOMException: Failed to execute 'put' on 'IDBObjectStore': HTMLCanvasElement object could not be cloned.
说明了你在使用 IndexedDB
存储数据时,尝试存储 HTMLCanvasElement
对象,而 HTMLCanvasElement
是一种不能直接克隆或序列化的 DOM 对象。
为什么会出现这个问题?
IndexedDB
使用结构化克隆算法(Structured Clone Algorithm)来存储和传输对象。这个算法支持存储大多数常见的 JavaScript 对象,如 Array
、Object
、Date
、Blob
等,但不支持某些 DOM 元素和其他复杂对象(例如 HTMLCanvasElement
、Window
、Function
、HTMLImageElement
等)。
HTMLCanvasElement
是一个DOM节点,不能直接被结构化克隆,因此在你尝试将它存储到 IndexedDB 时,会抛出 DOMException
错误。
解决方案
为了能够存储画布数据,你需要先将 HTMLCanvasElement
的内容提取成一种可以被结构化克隆的格式。通常做法是将画布转换成 ImageData
或者 Blob
,然后再存储到 IndexedDB。
方法1:将 HTMLCanvasElement
转换为 Blob
你可以使用 canvas.toBlob()
或者 canvas.toDataURL()
来将画布内容转换为 Blob
或 DataURL
,这样就能避免直接存储 HTMLCanvasElement
,并且可以将 Blob
存储到 IndexedDB。
javascriptCopy Code
function storeDrawing(canvas) { return new Promise((resolve, reject) => { // 将 canvas 转换为 Blob canvas.toBlob((blob) => { if (blob) { // 在此存储 Blob 到 IndexedDB let dbRequest = indexedDB.open('MyDatabase'); dbRequest.onsuccess = function(event) { let db = event.target.result; let transaction = db.transaction(['drawings'], 'readwrite'); let objectStore = transaction.objectStore('drawings'); // 这里存储一个包含 Blob 数据的对象 let drawingData = { id: Date.now(), // 唯一标识符 imageBlob: blob // 存储 Blob 数据 }; let putRequest = objectStore.put(drawingData); putRequest.onsuccess = function() { resolve('Canvas drawing stored successfully.'); }; putRequest.onerror = function(event) { reject('Failed to store drawing: ' + event.target.error); }; }; dbRequest.onerror = function(event) { reject('Failed to open database: ' + event.target.error); }; } else { reject('Failed to convert canvas to Blob.'); } }); }); }
方法2:将 HTMLCanvasElement
转换为 DataURL
另一种方式是将画布转换为 DataURL
,这通常是一种 base64 编码的字符串,适合较小的图像数据存储。然后可以将 DataURL
存储到 IndexedDB 中。
javascriptCopy Code
function storeDrawing(canvas) { return new Promise((resolve, reject) => { // 将 canvas 转换为 DataURL const dataURL = canvas.toDataURL(); // 存储 DataURL 到 IndexedDB let dbRequest = indexedDB.open('MyDatabase'); dbRequest.onsuccess = function(event) { let db = event.target.result; let transaction = db.transaction(['drawings'], 'readwrite'); let objectStore = transaction.objectStore('drawings'); let drawingData = { id: Date.now(), imageDataURL: dataURL // 存储 DataURL }; let putRequest = objectStore.put(drawingData); putRequest.onsuccess = function() { resolve('Canvas drawing stored successfully.'); }; putRequest.onerror = function(event) { reject('Failed to store drawing: ' + event.target.error); }; }; dbRequest.onerror = function(event) { reject('Failed to open database: ' + event.target.error); }; }); }
关键区别
- Blob:适合存储二进制数据,适合较大的图像。
- DataURL:适合存储小型图像,它会将图像数据编码为一个字符串,适合存储在数据库中,但它可能会比 Blob 占用更多的空间,因为它是 base64 编码的。
总结
你不能直接将 HTMLCanvasElement
存储到 IndexedDB 中,因为它无法被结构化克隆。需要先将画布转换为 Blob
或 DataURL
,然后存储转换后的数据。根据你的需求选择适合的方法(Blob
适合较大图像,DataURL
适合较小图像)。