Safari浏览器使用IndexedDB存储的File对象上传图片失败问题咨询
这是Safari浏览器长期存在的IndexedDB兼容性缺陷:存入IndexedDB的File/Blob对象仅会被序列化存储元数据(name、size、type、lastModified等)和表层二进制可读能力,内部的二进制流引用会在存储阶段丢失。因此从IndexedDB读出File后,你可以正常读取其属性、用FileReader转base64,但将其塞入FormData发起网络请求时,浏览器无法读取到实际的二进制内容,就会出现请求体中文件字段为空的情况。Chrome对IndexedDB的Blob/File序列化逻辑做了特殊兼容,因此不会触发该问题。
方案1(最稳定,全版本兼容):调整IndexedDB存储逻辑,存入前先把File转成ArrayBuffer
不要直接把原始File对象存入IndexedDB,先读取File的二进制内容为ArrayBuffer,同时存储File的元数据,读取时用存储的内容和元数据重新构造全新的File对象即可。
存入示例代码:// 存储前预处理 const originFile = 待存储的原始File对象; const fileBuffer = await originFile.arrayBuffer(); const saveData = { buffer: fileBuffer, name: originFile.name, type: originFile.type, lastModified: originFile.lastModified }; // 将saveData存入IndexedDB读取上传示例代码:
// 从IndexedDB读取到saveData后预处理 const restoredFile = new File([saveData.buffer], saveData.name, { type: saveData.type, lastModified: saveData.lastModified }); const formData = new FormData(); formData.append('upload_file', restoredFile); // 后续正常发起上传请求即可方案2(改动最小):走base64上传逻辑
既然你已经可以正常把读出的File转成base64 data url,直接把base64内容作为参数传给后端,后端解码base64得到二进制文件存储即可,不需要调整IndexedDB存储逻辑,只要前后端对齐传参格式就可以。方案3(临时兼容方案):读取旧存储的File后重建
如果你已经有大量历史数据直接存了File对象到IndexedDB,可以在读取到File后先主动重建二进制流,再构造新File上传:const indexedDBFile = 从IndexedDB读出的旧File对象; // 主动读取完整二进制流重建File const fixedBuffer = await indexedDBFile.slice().arrayBuffer(); const fixedFile = new File([fixedBuffer], indexedDBFile.name, { type: indexedDBFile.type, lastModified: indexedDBFile.lastModified }); // 用fixedFile上传即可
目前Safari 16及更低版本都存在该缺陷,Safari 17 beta版本仍未完全修复该问题,不建议依赖Safari对IndexedDB原生存储Blob/File的支持。
内容的提问来源于stack exchange,提问作者jnuyonug

