草庐IT

前端数据库indexedDB入门

YOLO浪漫收藏家 2023-04-09 原文

什么是indexDB

        学习文档:

        indexDB是HTML5的新概念,indexedDB是一个用于在浏览器中存储较大数据结构的Web API,并且提供了索引功能以实现高性能查找。不同于其他基于SQL的关系型数据库,indexedDB是一个事务型的数据库系统,会将数据集作为个体对象存储,数据形式使用的是JSON,而不是列数固定的表格来存储数据的。

        indexDB比本地存储很强大,而且存储大小是250m以上(受计算机硬件和浏览器厂商的限制)。

  • indexDB优点是:存储容量大;支持异步操作;具有事务特点;
  • indexDB缺点是:不支持DO操作;不能跨域。

        indexDB中的对象:

  • 数据库:IDBDatabase 对象
  • 对象仓库:IDBObjectStore 对象
  • 索引: IDBIndex 对象
  • 事务: IDBTransaction 对象
  • 操作请求:IDBRequest 对象
  • 指针(游标): IDBCursor 对象
  • 主键集合:IDBKeyRange 对象

        基本语法:

语法

作用

window.indexedDB.open(数据库名,版本号)

打开数据库(如果数据库不存在则创建一个新的库)

.onerror

数据库操作过程中出错时触发

.onupgradeneeded

创建一个新的数据库或者修改数据库版本号时触发

.onsuccess

数据库成功完成所有操作时触发

.createObjectStore(对象仓库名称,keypath)

创建对象仓库

.createIndex(索引名称,keypath,objectParameters)

建立索引

.transaction(对象仓库名称) || .transaction(对象仓库名称,‘readwrite’)

创建一个事务 || 创建一个事务,并要求具有读写权限

.objectStore(对象仓库名称)

获取对象仓库

.get ( num ) || .getAll()

获取数据 || 获取全部数据

.add( data )

添加数据

.put( newdata )

修改数据

.delete ( keypath )

删除数据

        Demo:

        关于具体应用的demo可以看我的gitee上的Demo:

https://gitee.com/yinlingyun123/dragVuehttps://gitee.com/yinlingyun123/dragVue

indexDB基础操作:

        indeDB和一般的数据库一样,创建/打开数据库,创建/打开表,对数据的增删查改等都是其基本功能。

        1、判断浏览器是否支持IndexDB

function justifyIndexDEB(){
    if("indexedDB" in window) {
        // 支持
        console.log(" 支持indexedDB...");
    } else {
        // 不支持
        console.log("不支持indexedDB...");
    }
}

        2、不同浏览器兼容

//数据库对象
window.indexedDB =window.indexedDB||window.webikitIndexedDB||window.mozIndexedDB||window.msIndexedDB;
//数据库事务
window.IDBTransaction = window.IDBTransaction||window.webikitIDBTransaction||window.mozIDBTransaction||window.msIDBTransaction;
//数据库查询条件
 window.IDBKeyRange = window.IDBKeyRange||window.webkitIDBKeyRange||window.mozIDBKeyRange||window.msIDBKeyRange;
//游标
window.IBDCursor =  window.IBDCursor ||window.webkitIBDCursor ||window.mozIBDCursor ||window.msIBDCursor ;

        3、创建/打开数据库

        如果存在就打开,不存在就创建一个indexDB数据库:

const req = window.indexedDB.open(databaseName, version)  // 数据库名称,版本号
let that = this;
req.onsuccess = function (event) { // 监听数据库创建成功事件
    that.indexDB = event.target.result // 数据库对象
    console.log('数据库打开成功')
}
req.onerror = function (error) {
    console.log('数据库打开报错')
}
req.onupgradeneeded = function (event) {
    // 数据库创建或升级的时候会触发
    console.log('数据库创建或升级')
}

        4、创建/打开/删除数据表

        在indexedDB中,是使用objectStore来存储数据呢。objectStore相当于一张表,但是objectStore并不想mysql中的表一样,具有一列一列的结构,它只有两列,一列是keypath(键值),另一列就是存储的数据了,存储的数据一般用JavaScript中的对象来表示。每一条数据都和一个键相关联。

        存储时可以使用每条记录中的某个指定字段作为键值(keyPath),也可以使用自动生成的递增数字作为键值(keyGenerator),也可以不指定。选择键的类型不同,objectStore可以存储的数据结构也有差异。

键类型

存储数据

不使用

任意值,但是没添加一条数据的时候需要指定键参数

keyPath

Javascript对象,对象必须有一属性作为键值

keyGenerator

任意值

都使用

Javascript对象,如果对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性

        创建表:

        通过数据库实例的createObjectStore(storeName,keyType)进行创建objectStore。这个方法有两个参数,一个是objectStore的名字,一个是创建表的键类型。

        创建表相当于修改了数据库的模式,所以这个操作应该放到onupgradeneeded中:

req.onupgradeneeded = function (event) {
    // 数据库创建或升级的时候会触发
    let db = event.target.result
    let storeName = 'product' // 表名
    if(!db.objectStoreNames.contains(storeName)) {
        // keyPath是主键键值,也可以不传然后设定autoIncrement:true自动创建键值
        db.createObjectStore(storeName,{keyPath:'key'});
    }
    //db.deleteObjectStore(storeName);删除数据仓库方法,参数为数据仓库名称
}

        5、创建/删除索引

        索引可以用来搜索,主键是默认的索引。

        只能针对被设为索引的属性值进行检索,不能针对没有被设为索引的属性值进行检索。

        索引包含有联合索引,唯一索引,对数组字段建索引等等扩展功能,有需要的可以自己探索!

req.onupgradeneeded = function (event) {
    // 数据库创建或升级的时候会触发
    db = event.target.result
    let storeName = 'product' // 表名
    if (!db.objectStoreNames.contains(storeName)) { // 判断表是否存在
        let objectStore = db.createObjectStore(storeName, { keyPath: 'key',autoIncrement: true })
        // 创建索引
        // indexName索引列名称
        // indexKey索引键值
        objectStore.createIndex('indexName', 'indexKey', { unique: false }) // 创建索引 可以让你搜索任意字段
        //objectStore.deleteIndex("indexName");删除索引
    }
}

        给对象仓库(数据库表)创建索引,需要使用对象仓库的createIndex()函数,param1 索引名称,param2 配置索引的键值,param3 配置对象 配置该属性是否是唯一的。

        param3 配置对象可配置属性:

  • unique 唯一
  • multiEntry 对于有多个值的主键数组,每个值将在索引里面新建一个条目,否则主键数组对应一个条目

        6、添加数据

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', "readwrite");
const store = transaction.objectStore('product');
const request = store.put({ key: 'item', value: '<p>3333</p>' });
// const request = store.add({ key: 'item', value: '<p>3333</p>' }); //add方法也可以
request.onsuccess=function(e) {
    console.info('添加数据成功')
};
request.onerror=function(e) {
    console.info('添加数据失败')
};

        7、获取数据

        get方法是获取定义的主键键值为输入数据的第一条数据,如果想获取匹配的所有数据就得用getAll方法进行获取。不过get、getAll方法需要查询整张表来获得结果,数据量大的时候效率很低。所以在日常使用中都是通过添加索引然后使用游标查询索引获取想要的数据。

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', 'readwrite');
const store = transaction.objectStore('product');
const request=store.get('item');
request.onsuccess=function(e) {
    store.put({ key: 'item', value: '<p>3333</p>' });
    console.info('获取数据成功')
};
request.onerror=function(e) {
    console.info('获取数据失败')
};

        8、更新数据

        更新数据操作其实就是添加和获取合二为一,在获取成功的回调函数里面进行数据的添加从而覆盖掉原来的数据:

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', 'readwrite');
const store = transaction.objectStore('product');
const request=store.get('item');
request.onsuccess=function(e) {
    store.put({ key: 'item', value: '<p>4444</p>' });
};

         9、删除数据

// this.indexDB创建数据库时候存下的数据库对象
// itemName是存储的key
// newValue是存储的value
const transaction = this.indexDB.transaction('product', 'readwrite');
const store = transaction.objectStore('product');
const request=store.delete('item');
request.onsuccess=function(e) {
    console.info('删除数据成功')
};
request.onerror=function(e) {
    console.info('删除数据失败')
};

        10、遍历数据

        使用指针对象IDBCursor遍历数据

objectStore.openCursor().onsuccess = function () {
   const cursor = e.target.result
   if (cursor) {
       console.log(cursor.key) // 当前遍历数据的主键
       console.log(cursor.value) // 当前遍历的数据
       cursor.continue() // 继续下一个
   }
}

        可以通过配置IDBKeyRange来设定查询范围

// 游标查询范围内的多个:
// 除了bound 还有 only,lowerBound, upperBound 方法,还可以指明是否排除边界值
const range = IDBKeyRange.bound([min1, min2, min3], [max1, max2, max3]) 
// 传入的 prev 表示是降序遍历游标,默认是next表示升序;如果索引不是unique的,而你又不想访问重复的索引,可以使用nextunique或prevunique,这时每次会得到key最小的那个数据
dbIndex.openCursor(range, "prev").onsuccess = e => {   
    let cursor = e.target.result;
    if (cursor) {
        let data = cursor.value  // 数据的处理就在这里。。。 [ 理解 cursor.key,cursor.primaryKey,cursor.value ]
        cursor.continue()
    } else {
        // 游标遍历结束!    
    }
}

        需要说明的是 IDBKeyRange.bound([min1, min2, min3], [max1, max2, max3])   的范围如下:

max1 > min1 || max1 === min1 && max2 > min2 || max1 === min1 && max2 === min2 && max3 > min3  // 好好理解一下这个 bound 的含义吧 ! 

        11、使用promise封装上述方法

        a.使用promise封装 open方法

/**
 * 打开/创建数据库
 * @param {object} dbName 数据库的名字
 * @param {string} storeName 仓库名称
 * @param {string} version 数据库的版本
 * @param {string} keyPath 主键键值,不传就自动创建主键
 * @param {Array} index 索引数组
 * @return {object} 该函数会返回一个数据库实例
 */
type StoreOptions = {
  autoIncrement: boolean;
  keyPath?: string;
};
export const openDB = function (
  dbName: string,
  version: number,
  storeName: string,
  keyPath?: string,
  index?: Array<any[]> | undefined
) {
  return new Promise((resolve, reject) => {
    //  兼容浏览器
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const { webkitIndexedDB, indexedDB, mozIndexedDB, msIndexedDB } = window;
    const indexDB = indexedDB || mozIndexedDB || webkitIndexedDB || msIndexedDB;
    let db = null;
    const request = indexDB.open(dbName, version);
    // 操作成功
    request.onsuccess = function (event: any) {
      db = event?.target?.result; // 数据库对象
      resolve({ code: 0, success: true, data: db, msg: "数据库打开成功!" });
    };
    // 操作失败
    request.onerror = function () {
      resolve({ code: -1, success: false, data: null, msg: "数据库打开失败!" });
    };
    // 创建表和索引
    request.onupgradeneeded = function (event: any) {
      // 数据库创建或升级的时候会触发
      db = event?.target?.result; // 数据库对象
      const storeOptions: StoreOptions = {
        autoIncrement: true,
      };
      if (keyPath && keyPath !== "") {
        storeOptions.autoIncrement = false;
        storeOptions.keyPath = keyPath;
      }
      // 创建表
      if (!db.objectStoreNames.contains(storeName)) {
        const store = db.createObjectStore(storeName, storeOptions);
        // 创建索引
        // indexName索引列名称
        // indexKey索引键值
        if (index && index.length > 0) {
          index.forEach((item: any) => {
            if (
              !item.indexName ||
              !item.indexKey ||
              item.options.unique === undefined
            ) {
              reject(
                "索引格式错误,请参照格式{indexName:'indexName',indexKey:'indexKey',{unique: false}}"
              );
            }
            store.createIndex(item.indexName, item.indexKey, item.options);
          });
        }
      }
    };
  });
};

        b.使用promise封装 add方法 

/**
 * 新增数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {object} dataConfig 添加的数据集合
 **/
export const addData = function (db: any, storeName: string, dataConfig: any) {
  return new Promise((resolve, reject) => {
    if (!db) {
      reject("数据库不存在或没有初始化");
    }
    if (!dataConfig || !dataConfig.value) {
      reject("value是必传项,参照格式{[keyPath]:'key',value:'value'}");
    }
    const req = db
      .transaction([storeName], "readwrite")
      .objectStore(storeName) // 仓库对象
      .add(dataConfig);
    // 操作成功
    req.onsuccess = function () {
      resolve({ code: 0, success: true, data: null, msg: "数据写入成功!" });
    };
    // 操作失败
    req.onerror = function () {
      const data = {
        code: -1,
        success: false,
        data: null,
        msg: "数据写入失败!",
      };
      resolve(data);
    };
  });
};

        c.使用promise封装 put方法 

/**
 * 更新数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {object} dataConfig 更新的数据集合
 */
export const updateData = function (
  db: any,
  storeName: string,
  dataConfig: any
) {
  return new Promise((resolve, reject) => {
    if (!db) {
      reject("数据库不存在或没有初始化");
    }
    if (!dataConfig || !dataConfig.value) {
      reject("value是必传项,参照格式{[keyPath]:'key',value:'value'}");
    }
    const req = db
      .transaction([storeName], "readwrite")
      .objectStore(storeName)
      .put(dataConfig);
    // 操作成功
    req.onsuccess = function () {
      resolve({ code: 0, success: true, data: null, msg: "数据更新成功!" });
    };
    // 操作失败
    req.onerror = function () {
      const data = {
        code: -1,
        success: false,
        data: null,
        msg: "数据更新失败!",
      };
      resolve(data);
    };
  });
};

        d.使用promise封装 get方法

/**
 * 查询数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} key 数据主键
 **/
export const getData = function (db: any, storeName: string, key: string) {
  return new Promise((resolve, reject) => {
    if (!db) {
      reject("数据库不存在或没有初始化");
    }
    const req = db
      .transaction([storeName], "readonly")
      .objectStore(storeName) // 仓库对象
      .get(key);
    // 操作成功
    req.onsuccess = function (e: { target: { result: any } }) {
      resolve({
        code: 0,
        success: true,
        data: e?.target?.result,
        msg: "数据获取成功!",
      });
    };
    // 操作失败
    req.onerror = function () {
      const data = {
        code: -1,
        success: false,
        data: null,
        msg: "数据获取失败!",
      };
      resolve(data);
    };
  });
};

        d.使用promise封delete方法 

/**
 * 删除数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} key 数据主键
 **/
export const deleteData = function (db: any, storeName: string, key: string) {
  return new Promise((resolve, reject) => {
    if (!db) {
      reject("数据库不存在或没有初始化");
    }
    const req = db
      .transaction([storeName], "readwrite")
      .objectStore(storeName) // 仓库对象
      .delete(key);
    // 操作成功
    req.onsuccess = function (e: { target: { result: any } }) {
      resolve({
        code: 0,
        success: true,
        data: e?.target?.result,
        msg: "数据删除成功!",
      });
    };
    // 操作失败
    req.onerror = function () {
      const data = {
        code: -1,
        success: false,
        data: null,
        msg: "数据删除失败!",
      };
      resolve(data);
    };
  });
};

        e.使用promise封装 游标查询方法 

/**
 * 使用游标查询数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexKey 查询的索引的键值
 * @param {string} index 查询的索引值
 **/
export const getIndexData = function (
  db: any,
  storeName: string,
  indexKey: string,
  index: string
) {
  return new Promise((resolve, reject) => {
    if (!db) {
      reject("数据库不存在或没有初始化");
    }
    const keyRange = IDBKeyRange.only(index);
    const req = db
      .transaction([storeName], "readonly")
      .objectStore(storeName) // 仓库对象
      .index(indexKey)
      .openCursor(keyRange, "next");
    // 操作成功
    req.onsuccess = function (e: { target: { result: any } }) {
      resolve({
        code: 0,
        success: true,
        data: e?.target?.result,
        msg: "数据查询成功!",
      });
    };
    // 操作失败
    req.onerror = function () {
      const data = {
        code: -1,
        success: false,
        data: null,
        msg: "数据查询失败!",
      };
      resolve(data);
    };
  });
};

        详细封装文件查看请戳链接:

https://gitee.com/yinlingyun123/dragVue/blob/master/src/utils/indexDB.tsxhttps://gitee.com/yinlingyun123/dragVue/blob/master/src/utils/indexDB.tsx

indexDB常见错误:

        1.transaction出错

Uncaught DOMException: Failed to execute ‘transaction’ on ‘IDBDatabase’: A version change transaction is running.

        错误原因:

        有一个事务在运行,不能打开第二个事务.

        解决办法:

        将transaction相关的代码写在objectStore.transaction.oncomplete中

var request = indexedDB.open('sql', 2);
request.onupgradeneeded = function(event) {
    var db = event.target.result;
    var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
    objectStore.transaction.oncomplete = function(event) {
        var customerObjectStore = db.transaction("customers", "readwrite")
        .objectStore("customers")
        .add({ id: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" });
    };
};

        2.Cannot read错误

Uncaught TypeError: Cannot read property ‘xxx’ of undefined

        错误原因:

        因为indexDB代码都是异步代码,所以有些还没有定义部分的代码可能会优先执行,导致这个变量为undefined

        3.尝试读取数据时候报错

Uncaught InvalidStateError: Failed to read the ‘result’ property from ‘IDBRequest’: The request has not finished

        错误示范:

var r = indexedDB.open();
var db = null;
r.onsuccess = function(event) { db = event.target.result); }

        正确示范:

var r = indexedDB.open();
r.onsuccess = function(event) {
    var db = event.target.result;
};

        4.Put的时候报错

failed to execute ‘put’ on ‘idbobjectstore’ evaluating the object store’s key path did not yield a value

        错误原因:        

        储存数据的时候必须要带上object store的key一起储存,或者使用一个key:generator({autoIncrement: true})

        示例:

var store = db.createObjectStore('my_store', {keyPath: 'key'});
store.put({key: 11, value: 33}); // OK
store.put({value: 66}); // throws, since 'key' is not present
var store = db.createObjectStore('my_store', {keyPath: 'key', autoIncrement: true});
store.put({key: 11, value: 33}); // OK, key generator set to 11
store.put({value: 66}); // OK, will have auto-generated key 12

        5.createObjectStore undefined

Cannot read property ‘createObjectStore’ of undefined

       错误原因:   

        这是因为indexedDB是异步的,你必须在回调函数中使用createObjectStore方法,即使你把createObjectStore调用写在open函数后面,也无法保证哪个先完成。

       6.Failed to execute ‘createObjectStore’

Failed to execute ‘createObjectStore’ on ‘IDBDatabase’: The database is not running a version change transaction.

       错误原因:           

        这是由于在success事件的回调中调用createObjectStore方法,该方法应该在upgradeneeded事件的回调中调用。

        7.stores was not found.

Failed to exectue ‘transaction’ on ‘IDBDatabase’: One of the specified stores was not found.

        错误原因:          

        这是因为upgradeneeded事件没有被触发。

        这里需要注意upgradeneeded事件。首先,根据API,应该在upgradneeded事件的回调函数中调用createObjectStore方法创建store object,不应该在success的回调中,否则会报错。其次,当为open方法传入一个本域没有的数据库名时,会创建相应的数据库,并触发success、upgradeneeded事件,从而创建一个store object。但是,chrome54并不会触发upgradeneeded事件,造成store object不会被创建,后续在store object上创建事务并操作数据时候就会报错。Stackoverflow上提供的解决办法是,在open方法传入第二个参数(与已有version不同,且更大),这样就会触发chrome上的upgradeneeded事件了。不过,每次都需要调用db.version获取当前的版本号。

        8.add出现错误

Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.

        错误原因:

        transaction结束了,不能继续使用,所以才需要每次操作数据库都要获得一个

其他

        IDBOpenDBRequest还有一个类似回调函数句柄——onupgradeneeded。

        该句柄在我们请求打开的数据库的版本号和已经存在的数据库版本号不一致的时候调用。

        indexedDB.open方法还有第二个可选参数,数据库版本号,数据库创建的时候默认版本号为1,当我们传入的版本号和数据库当前版本号不一致的时候onupgradeneeded就会被调用,当然我们不能试图打开比当前数据库版本低的version.

        代码中定义了一个myDB对象,在创建indexedDB request的成功毁掉函数中,把request获取的DB对象赋值给了myDB的db属性,这样就可以使用myDB.db来访问创建的indexedDB了。

        用indexedBD的时候要善用onerror来获取错误的信息,这样就知道哪里出错了。

参考文章链接:

有关前端数据库indexedDB入门的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  3. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  4. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  5. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  6. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  7. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

  8. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  9. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  10. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

随机推荐