Советы по работе с базами данных

SQLite 3 в действии: как использовать API доступа к файловой системе

Поняв, как создавать, изменять и загружать базу данных SQLite 3 в браузере, сегодня я хочу попытаться понять, как открывать, читать и изменять локально сохраненную базу данных. Меня интересует эмуляция поведения настольного приложения, сохраняющего данные в базу данных SQLite 3. Для этого я снова буду использовать версию SQLite 3 на JavaScript, скомпилированную в WebAssembly. Также я буду использовать API доступа к файловой системе. Для обработки графики я буду использовать Svelte.

Но сначала рекомендую прочитать первую и вторую части этой серии:

Как открыть локальную базу данных

В отличие от сообщения, опубликованного несколько дней назад, я намерен использовать API доступа к файловой системе. Таким образом, я могу получить доступ к локальному файлу, прочитать его, изменить и затем сохранить. Конечным результатом является опыт, очень похожий на настольное приложение.

Сначала мне нужна функция, которая может открыть локальный файл. Эта функция очень проста:

let fileHandle;

const openFile = async () => {
  [fileHandle] = await globalThis.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.arrayBuffer();
};

По сути, функция showOpenFilePicker открывает диалоговое окно, позволяющее выбрать файл. После выбора возвращается объект FileHandle. Этот объект содержит выбранный файл. Чтобы прочитать файл, я использую метод getFile, а затем arrayBuffer для получения содержимого файла. В дополнение к arrayBuffer существуют также методы text и stream, которые возвращают содержимое файла в виде строки и ReadableStream соответственно.

Чтобы получить базу данных из файла, я могу повторно использовать функцию readDatabase():

const readDatabase = (arrayBuffer) => {
  const sqlite3 = self["sqlite3"];
  let bytes = new Uint8Array(arrayBuffer);
  const p = sqlite3.wasm.allocFromTypedArray(bytes);
  let db = new sqlite3.oo1.DB();

  let rc = sqlite3.capi.sqlite3_deserialize(
    db.pointer,
    "main",
    p,
    bytes.length,
    bytes.length,
    sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE |
      sqlite3.capi.SQLITE_DESERIALIZE_RESIZEABLE
  );
  db.checkRc(rc);

  return db;
};

Таким образом я получаю:

let fileHandle;

const openFile = async () => {
  [fileHandle] = await globalThis.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.arrayBuffer();
  const db = readDatabase(contents);
  return db;
};

Я держу переменную fileHandle вне функции, потому что использую ее для сохранения файла. Теперь я могу создать специальную кнопку для открытия файла:

<button
  on:click={async () => {
      db = await openFile();
      listTable = [...getTableList(db)];
    }}
  >Open File</button>

Как сохранить базу данных SQLite 3

Чтобы сохранить базу данных SQLite 3, я снова использую API доступа к файловой системе. Сначала я получаю дескриптор файла и делаю его доступным для записи:

const writable = await handle.createWritable();

Я использую метод sqlite3_js_db_export для получения массива байтов:

const byteArray = self["sqlite3"].capi.sqlite3_js_db_export(db.pointer);

Наконец, я создаю большой двоичный объект и записываю его в файл:

const blob = new Blob([byteArray.buffer], {
  type: "application/x-sqlite3",
});

await writable.write(blob);
await writable.close();

Окончательный результат:

const saveFile = async (handle, db) => {
  const writable = await handle.createWritable();
  const byteArray = self["sqlite3"].capi.sqlite3_js_db_export(db.pointer);
  
  const blob = new Blob([byteArray.buffer], {
    type: "application/x-sqlite3",
  });
  
  await writable.write(blob);
  await writable.close();
};

Теперь я могу добавить кнопку для сохранения файла:

<button on:click={async () => await saveFile(fileHandle, db)}
  >Save File</button
>

Как сохранить базу данных SQLite 3 в новый файл

Я могу сохранить базу данных SQLite 3 в новом файле. Для этого я снова использую API доступа к файловой системе. Я также могу использовать функцию saveFile, которую я определил ранее, но сначала мне нужен новый дескриптор файла:

const showSaveFilePicker = async () => {
  const options = {
    types: [
      {
        description: "SQLite 3",
        accept: {
          "application/x-sqlite3": [".sqlite", ".sqlite3", ".db"],
        },
      },
    ],
  };
  const handle = await globalThis.showSaveFilePicker(options);
  return handle;
};

Итак, я добавляю кнопку:

<button
  on:click={async () => {
    const handle = await showSaveFilePicker();
    await saveFile(handle, db);
  }}>Save File As</button
>

И это все, по крайней мере, на сегодня.

Спасибо за прочтение! Оставайтесь с нами, чтобы узнать больше.

Не пропустите мою следующую статью — подпишитесь на мой средний список адресов электронной почты



Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.