React Native中SQLite异步存储Blob/Base64图片并读取显示问题
解决方案:SQLite异步插入Blob图片问题
问题根源
- 原代码中
result.map(async item => ...)生成的异步函数未被等待,事务可能在convertImageToBase64返回结果前提前结束,导致插入空值。 tx.executeSql是回调式异步操作,未包装为Promise时无法用await控制执行顺序,造成事务内操作混乱。- 若需存储Blob类型,直接存入Base64字符串不符合需求,需转换为Blob对象。
方案一:顺序异步处理(适合需要保证插入顺序的场景)
db.transaction(async (tx) => { // 用for...of循环替代map,确保每个异步操作完成后再执行下一个 for (const item of result) { try { // 等待Base64转换完成 const base64Data = await convertImageToBase64(item.imgUrl); // 将Base64转换为Blob对象 const imgBlob = base64ToBlob(base64Data); // 将tx.executeSql包装为Promise,确保插入完成后再继续 await new Promise((resolve, reject) => { tx.executeSql( "INSERT INTO markets (name, imgUrl, done) VALUES (?,?,?)", [item.name, imgBlob, item.done], (tx, results) => { console.log("插入成功,影响行数:", results.rowsAffected); resolve(); }, (error) => { console.error("插入失败:", error); reject(error); } ); }); } catch (err) { console.error("处理数据失败:", err); throw err; // 抛出错误触发事务回滚 } } }); // Base64转Blob辅助函数 function base64ToBlob(base64) { const parts = base64.split(';base64,'); const contentType = parts[0].split(':')[1]; const rawData = window.atob(parts[1]); const uint8Array = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; i++) { uint8Array[i] = rawData.charCodeAt(i); } return new Blob([uint8Array], { type: contentType }); }
方案二:批量预处理+插入(适合性能优先、无需严格顺序的场景)
先批量完成所有图片的Base64转Blob操作,再统一执行事务插入:
// 第一步:批量处理所有图片数据 const processedItems = await Promise.all( result.map(async (item) => { const base64Data = await convertImageToBase64(item.imgUrl); const imgBlob = base64ToBlob(base64Data); return { ...item, imgBlob }; }) ); // 第二步:在事务中批量插入 db.transaction(async (tx) => { for (const item of processedItems) { await new Promise((resolve, reject) => { tx.executeSql( "INSERT INTO markets (name, imgUrl, done) VALUES (?,?,?)", [item.name, item.imgBlob, item.done], (tx, results) => { console.log("插入成功,影响行数:", results.rowsAffected); resolve(); }, (error) => { console.error("插入失败:", error); reject(error); } ); }); } }); // 复用Base64转Blob辅助函数 function base64ToBlob(base64) { const parts = base64.split(';base64,'); const contentType = parts[0].split(':')[1]; const rawData = window.atob(parts[1]); const uint8Array = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; i++) { uint8Array[i] = rawData.charCodeAt(i); } return new Blob([uint8Array], { type: contentType }); }
额外说明
- 若你的SQLite封装(如某些WebSQL兼容库)不直接支持Blob参数,可将Blob转换为ArrayBuffer后插入,部分库会自动识别并存储为Blob类型。
- 读取图片时,从数据库取出Blob对象后,可通过
URL.createObjectURL(blob)生成可用于<img>标签的URL。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

