Angular集成DuckDB-Wasm无法加载查询Parquet文件求助
我在Angular中尝试用DuckDB-Wasm加载并查询Parquet文件,已经成功建立连接,但加载文件时触发以下错误:
Error: Uncaught (in promise): Error: IO Error: No files found that match the pattern "test.parquet" Error: IO Error: No files found that match the pattern "test.parquet" at O.onMessage (duckdb-browser.mjs:1:11169) at _ZoneDelegate.invokeTask (zone.js:402:31) at core.mjs:10757:55 at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:10757:36) at _ZoneDelegate.invokeTask (zone.js:401:60) at Object.onInvokeTask (core.mjs:11070:33) at _ZoneDelegate.invokeTask (zone.js:401:60) at Zone.runTask (zone.js:173:47) at ZoneTask.invokeTask [as invoke] (zone.js:483:34) at invokeTask (zone.js:1631:18) at resolvePromise (zone.js:1193:31) at zone.js:1100:17 at zone.js:1116:33 at asyncGeneratorStep (asyncToGenerator.js:6:1) at _throw (asyncToGenerator.js:25:1) at _ZoneDelegate.invoke (zone.js:368:26) at Object.onInvoke (core.mjs:11083:33) at _ZoneDelegate.invoke (zone.js:367:52) at Zone.run (zone.js:129:43) at zone.js:1257:36
我的服务代码如下:
import { Injectable } from '@angular/core'; import * as duckdb from '@duckdb/duckdb-wasm'; @Injectable({ providedIn: 'root', }) export class DuckdbService { constructor() { this.makeConnection(); } async makeConnection() { const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles(); // Select a bundle based on browser checks const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES); const worker_url = URL.createObjectURL( new Blob([`importScripts("${bundle.mainWorker}");`], { type: 'text/javascript', }) ); // Instantiate the asynchronus version of DuckDB-Wasm const worker = new Worker(worker_url); const logger = new duckdb.ConsoleLogger(); const db = new duckdb.AsyncDuckDB(logger, worker); await db.instantiate(bundle.mainModule, bundle.pthreadWorker); console.log(db); const conn = await db.connect(); const results = conn.query(`SELECT * FROM read_parquet('test.parquet')`); console.log(results); URL.revokeObjectURL(worker_url); } }
问题核心原因
DuckDB-Wasm运行在浏览器沙箱环境中,无法直接访问本地文件系统或Angular项目中的文件路径。你直接传入的test.parquet字符串没有指向任何DuckDB可识别的虚拟文件系统位置,因此触发找不到文件的错误。同时原代码中conn.query缺少await,会导致异步操作未正确处理。
解决方法
1. 加载用户上传的本地Parquet文件
通过浏览器文件上传控件获取用户本地的Parquet文件,再将其注册到DuckDB的虚拟文件系统中:
修改后的服务代码
import { Injectable } from '@angular/core'; import * as duckdb from '@duckdb/duckdb-wasm'; @Injectable({ providedIn: 'root', }) export class DuckdbService { private db: duckdb.AsyncDuckDB | null = null; private conn: duckdb.AsyncDuckDBConnection | null = null; constructor() { this.initDuckDB(); } // 单独封装初始化逻辑,避免构造函数异步调用问题 async initDuckDB() { const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles(); const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES); const worker_url = URL.createObjectURL( new Blob([`importScripts("${bundle.mainWorker}");`], { type: 'text/javascript', }) ); const worker = new Worker(worker_url); const logger = new duckdb.ConsoleLogger(); this.db = new duckdb.AsyncDuckDB(logger, worker); await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker); this.conn = await this.db.connect(); URL.revokeObjectURL(worker_url); } async loadAndQueryUserFile(file: File) { if (!this.db || !this.conn) { throw new Error('DuckDB连接未初始化完成'); } // 将文件注册到DuckDB虚拟文件系统 await this.db.registerFileHandle('test.parquet', file, duckdb.FileAccessMode.READ); // 异步执行查询并返回结果 const results = await this.conn.query(`SELECT * FROM read_parquet('test.parquet')`); return results; } }
配套组件代码
import { Component } from '@angular/core'; import { DuckdbService } from './duckdb.service'; @Component({ selector: 'app-parquet-loader', template: `<input type="file" accept=".parquet" (change)="onFileSelected($event)">`, }) export class ParquetLoaderComponent { constructor(private duckdbService: DuckdbService) {} async onFileSelected(event: Event) { const file = (event.target as HTMLInputElement).files?.[0]; if (file) { try { const queryResults = await this.duckdbService.loadAndQueryUserFile(file); // 在这里处理查询结果,比如渲染到页面 console.log('查询结果:', queryResults); } catch (err) { console.error('加载查询失败:', err); } } } }
2. 加载Angular Assets目录中的Parquet文件
如果文件放在项目的assets目录下,需要先通过HTTP请求获取文件Blob,再注册到虚拟文件系统:
服务中添加加载Assets文件的方法
async loadAndQueryAssetsFile() { if (!this.db || !this.conn) { throw new Error('DuckDB连接未初始化完成'); } // 从Assets目录获取文件 const response = await fetch('assets/test.parquet'); const blob = await response.blob(); const file = new File([blob], 'test.parquet'); // 注册并查询 await this.db.registerFileHandle('test.parquet', file, duckdb.FileAccessMode.READ); const results = await this.conn.query(`SELECT * FROM read_parquet('test.parquet')`); return results; }
内容的提问来源于stack exchange,提问作者sharath222

