大文件切割上传到IndexedDB

20 阅读2分钟

核心实现思路

IndexedDB 是异步操作的 API,核心步骤分为:

  1. 打开 / 创建数据库(指定名称和版本)
  2. 监听数据库升级事件(用于创建 / 修改对象仓库)
  3. 处理打开成功 / 失败的回调
  4. 封装常用的增删查操作

创建一个公共文件

// IndexedDB 操作的工具函数
class IndexedDBHelper {
    constructor(dbName, storeName, version = 1) {
        this.dbName = dbName; // 数据库名称
        this.storeName = storeName; // 对象仓库名称(类似表名)
        this.version = version; // 数据库版本
        this.db = null; // 保存打开的数据库实例
    }

    // 打开数据库的核心方法
    openDB() {
        return new Promise((resolve, reject) => {
            // 1. 调用 indexedDB.open 打开/创建数据库
            const request = indexedDB.open(this.dbName, this.version);

            // 2. 数据库升级事件(版本号提升时触发,用于初始化/修改结构)
            request.onupgradeneeded = (event) => {
                this.db = event.target.result;
                console.log('数据库版本升级,当前版本:', event.oldVersion);

                // 判断对象仓库是否存在,不存在则创建(指定主键)
                if (!this.db.objectStoreNames.contains(this.storeName)) {
                    // 创建对象仓库,指定主键为 id,且自增
                    const objectStore = this.db.createObjectStore(this.storeName, {
                        keyPath: 'id',
                        autoIncrement: true
                    });
                    // 可选:创建索引,方便查询(比如按 name 索引)
                    objectStore.createIndex('nameIndex', 'name', { unique: false });
                }
            };

            // 3. 数据库打开成功
            request.onsuccess = (event) => {
                this.db = event.target.result;
                console.log('数据库打开成功');
                resolve(this.db); // 成功回调返回数据库实例
            };

            // 4. 数据库打开失败
            request.onerror = (event) => {
                console.error('数据库打开失败:', event.target.error);
                reject(event.target.error); // 失败回调返回错误
            };

            // 5. 数据库被意外关闭
            request.onblocked = () => {
                console.error('数据库被占用,无法打开/升级');
                reject(new Error('数据库被占用,请关闭其他标签页后重试'));
            };
        });
    }

    // 封装:添加数据
    addData(data) {
        if (!this.db) throw new Error('数据库未打开');

        // 创建事务,指定操作的仓库和读写模式
        const transaction = this.db.transaction(this.storeName, 'readwrite');
        const store = transaction.objectStore(this.storeName);
        const request = store.add(data);

        return new Promise((resolve, reject) => {
            request.onsuccess = () => resolve('数据添加成功');
            request.onerror = (e) => reject(e.target.error);
        });
    }
    /**读取数据库数据*/
    getAllData(id) {
        if (!this.db) throw new Error('数据库未打开');
        const transaction = this.db.transaction(this.storeName, 'readonly');
        const store = transaction.objectStore(this.storeName);
        let request = null
        if (id) {
            request = store.get(id);

        } else {
            request = store.getAll();

        }

        return new Promise((resolve, reject) => {
            request.onsuccess = (e) => resolve(e.target.result);
            request.onerror = (e) => reject(e.target.error);
        });
    }
    /**根据主键删除数据*/ 
    deleteData(id) {
        if (!this.db) throw new Error('数据库未打开');
        const transaction = this.db.transaction(this.storeName, 'readwrite');
        const store = transaction.objectStore(this.storeName);
        const request = store.delete(id);

        return new Promise((resolve, reject) => {
            request.onsuccess = () => resolve('数据删除成功');
            request.onerror = (e) => reject(e.target.error);
        });
    }
    // 封装:关闭数据库
    closeDB() {
        if (this.db) {
            this.db.close();
            console.log('数据库已关闭');
        }
    }
}

// ===================== 调用示例 =====================
// 1. 创建数据库助手实例(数据库名:myDB,对象仓库名:user,版本:1)
export const dbHelper = new IndexedDBHelper('myDB', 'user', 1);



// 页面关闭时关闭数据库
window.addEventListener('beforeunload', () => {
    dbHelper.closeDB();
});

导入需要使用到IndexedDB import { dbHelper } from './dbHelper.js'

将文件切割好装入IndexedDB

/**切割好的文件Blob*/
let fileList = null

let file = ref(null)
async function handleFileChange(event) {
  file.value = event.target.files[0]
  fileList = _fileCutting(file.value, 10 * 1024 * 1024)
  try {
    // 打开数据库
    await dbHelper.openDB()
    // 增加数据
    await dbHelper.addData({ name: '文件', fileList })
  } catch (error) {
    console.log(error)
  }
}
/**切割file*/
function _fileCutting(file, size) {
  let fileList = []
  for (let i = 0; i < file.size; i += size) {
    fileList.push(file.slice(i, i + size))
  }
  return fileList
}