Skip to main content

Ionic 应用中的数据传导

Ionic 是一个移动应用框架,用于使用 Cordova 平台构建 iOS 和 Android 应用。

¥Ionic is a mobile app framework for building iOS and Android apps with the Cordova platform.

SheetJS 是一个用于从电子表格读取和写入数据的 JavaScript 库。

¥SheetJS is a JavaScript library for reading and writing data from spreadsheets.

该演示使用 Ionic 和 SheetJS 处理数据并生成电子表格。我们将探讨如何在 Ionic 应用中加载 SheetJS,并使用 Ionic API 和插件从设备上的电子表格文件中提取数据并向其中写入数据。

¥This demo uses Ionic and SheetJS to process data and generate spreadsheets. We'll explore how to load SheetJS in an Ionic app and use Ionic APIs and plugins to extract data from, and write data to, spreadsheet files on the device.

"演示" 创建一个应用,如下图所示:

¥The "Demo" creates an app that looks like the screenshots below:

iOSAndroid

iOS screenshot

Android screenshot

该演示涵盖使用 Cordova 平台的 Ionic 应用。

¥This demo covers Ionic apps using the Cordova platform.

CapacitorJS 演示 涵盖 CapacitorJS 应用。

¥The CapacitorJS demo covers CapacitorJS apps.

测试部署

本 demo 在以下环境下进行了测试:

¥This demo was tested in the following environments:

真实设备

¥Real Devices

OS设备配置日期
安卓 30英伟达盾A2024-05-30
iOS 15.1iPad ProA2024-05-30

模拟器

¥Simulators

OS设备配置开发平台日期
安卓 34像素 3aAdarwin-arm2024-05-30
iOS 17.5iPhone SE(第 3 代)Adarwin-arm2024-05-30
Configurations (click to show)

Configuration A:

  • Ionic: @ionic/angular 8.2.0, @ionic/angular-toolkit 11.0.1
  • Cordova: cordova-lib@12.0.1, android 13.0.0, ios 7.1.0
  • File Integration: @awesome-cordova-plugins/file version 6.7.0
遥测

在开始此演示之前,请手动禁用遥测。在 Linux 和 MacOS 上:

¥Before starting this demo, manually disable telemetry. On Linux and MacOS:

rm -rf ~/.ionic/
mkdir ~/.ionic
cat <<EOF > ~/.ionic/config.json
{
"version": "6.20.1",
"telemetry": false,
"npmClient": "npm"
}
EOF
npx @capacitor/cli telemetry off

要验证遥测是否已禁用:

¥To verify telemetry was disabled:

npx @ionic/cli config get -g telemetry
npx @capacitor/cli telemetry

集成详情

¥Integration Details

SheetJS NodeJS 模块 可以从应用中的任何组件或脚本导入。

¥The SheetJS NodeJS Module can be imported from any component or script in the app.

内部状态

¥Internal State

"Angular" 演示 讨论了一些状态表示和预览策略。

¥The "Angular" demo discusses a number of state representations and preview strategies.

对于此演示,内部状态是 "数组的数组"[^1] (any[][]):

¥For this demo, the internal state is an "array of arrays"[^1] (any[][]):

Array of Arrays state
import { Component } from '@angular/core';
type AOA = any[][];

@Component({...})
export class SheetJSTablePage {
data: AOA = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
// ...
}

显示数据

¥Displaying Data

ion-grid[^2] 是一个显示网格组件。Angular ngFor 指令 [^3] 简化了数组数组的迭代:

¥ion-grid[^2] is a display grid component. The Angular ngFor directive[^3] simplifies iteration over the array of arrays:

Template for displaying an array of arrays
<ion-grid>
<ion-row *ngFor="let row of data">
<ion-col *ngFor="let val of row">
{{val}}
</ion-col>
</ion-row>
</ion-grid>

文件操作

¥File Operations

cordova-plugin-file 插件在设备上读取和写入文件。

¥The cordova-plugin-file plugin reads and writes files on devices.

对于 Android 30+,由于范围存储规则,标准文件模块会写入无法从文件应用访问的私有文件。

¥For Android 30+, due to scoped storage rules, the standard file module writes private files that cannot be accessed from the Files app.

必须使用存储访问框架插件来写入外部文件。

¥A Storage Access Framework plugin must be used to write external files.

@awesome-cordova-plugins/file 是一个专为 Ionic + Angular 应用设计的封装器。

¥@awesome-cordova-plugins/file is a wrapper designed for Ionic + Angular apps.

@ionic-native 范围内的插件已被弃用。应使用 @awesome-cordova-plugins 范围内的社区模块。

¥The plugins in the @ionic-native scope have been deprecated. The community modules in the @awesome-cordova-plugins scope should be used.

读取文件

¥Reading Files

this.file.readAsArrayBuffer 从指定的 URL 读取文件数据并解析为 ArrayBuffer 对象。

¥this.file.readAsArrayBuffer reads file data from a specified URL and resolves to ArrayBuffer objects.

这些对象可以使用 SheetJS read 方法 [^4] 进行解析。带有选项 header: 1 的 SheetJS sheet_to_json 方法 [^5] 生成一个可以分配给页面状态的数组数组:

¥These objects can be parsed with the SheetJS read method[^4]. The SheetJS sheet_to_json method[^5] with the option header: 1 generates an array of arrays which can be assigned to the page state:

/* read a workbook file */
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, filename);
/* parse */
const wb: XLSX.WorkBook = XLSX.read(ab, {type: 'array'});
/* generate an array of arrays from the first worksheet */
const ws: XLSX.WorkSheet = wb.SheetNames[wb.Sheets[0]];
const aoa: AOA = XLSX.utils.sheet_to_json(ws, {header: 1});
/* update state */
this.data = aoa;

写入文件

¥Writing Files

this.file.writeFile 将存储在 Blob 对象中的文件数据写入设备。

¥this.file.writeFile writes file data stored in Blob objects to the device.

SheetJS aoa_to_sheet 方法 [^6] 从数组的数组中生成一个工作表对象。book_newbook_append_sheet 辅助程序 [^7] 生成一个工作簿对象。带有选项 type: "array" 的 SheetJS write 方法 [^8] 将生成 ArrayBuffer,从中可以创建 Blob

¥From the array of arrays, the SheetJS aoa_to_sheet method[^6] generates a worksheet object. The book_new and book_append_sheet helpers[^7] generate a workbook object. The SheetJS write method[^8] with the option type: "array" will generate an ArrayBuffer, from which a Blob can be created:

/* generate worksheet */
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);

/* generate workbook and add the worksheet */
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');

/* write XLSX to ArrayBuffer */
const ab: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });

/* generate Blob */
let blob = new Blob([ab], {type: 'application/octet-stream'});

/* write Blob to device */
this.file.writeFile(url, filename, blob, {replace: true});

演示

¥Demo

此演示中的应用将在表格中显示数据。

¥The app in this demo will display data in a table.

加载时,将处理 测试文件

¥On load, a test file will be processed.

当使用文件选择器选择文档时,它将被处理并且表格将刷新以显示内容。

¥When a document is selected with the file picker, it will be processed and the table will refresh to show the contents.

"导入数据" 将尝试从已知位置读取 SheetJSIonic.xlsx。警报将显示预期位置。

¥"Import Data" will attempt to read SheetJSIonic.xlsx from a known location. An alert will display the expected location.

"导出数据" 将尝试将表数据导出到已知位置的 SheetJSIonic.xlsx。写入后,警报将显示文件的位置。

¥"Export Data" will attempt to export the table data to SheetJSIonic.xlsx in a known location. After writing, an alert will display the location of the file.

平台设置

¥Platform Setup

  1. 按照警告中的说明禁用遥测。

    ¥Disable telemetry as noted in the warning.

  2. 遵循 iOS 和 Android 开发的官方说明 [^9]。

    ¥Follow the official instructions for iOS and Android development[^9].

Installation Notes (click to show)

Ionic requires Java 17.

  1. 安装所需的全局依赖:

    ¥Install required global dependencies:

npm i -g cordova cordova-res @angular/cli native-run @ionic/cli

在某些系统中,必须以 root 用户身份运行该命令:

¥In some systems, the command must be run as the root user:

sudo npm i -g cordova cordova-res @angular/cli native-run @ionic/cli

基础项目

¥Base Project

  1. 创建一个新项目:

    ¥Create a new project:

ionic start SheetJSIonic blank --type angular --cordova --quiet --no-git --no-link --confirm

当要求选择 NgModulesStandalone Components 时,选择 NgModules

¥When asked to select NgModules or Standalone Components, select NgModules

如果提示要求确认使用 Cordova,请输入 Yes 继续。

¥If a prompt asks to confirm Cordova use, enter Yes to continue.

如果提示询问是否创建 Ionic 账户,请输入 N 以选择退出。

¥If a prompt asks about creating an Ionic account, enter N to opt out.

由于依赖树中的冲突,该命令在某些测试运行中失败。

¥Due to conflicts in the dependency tree, the command failed in some test runs.

如果包安装失败,请强制安装所有模块:

¥If the package installation fails, forcefully install all modules:

cd SheetJSIonic
npm i --force @angular/cli
npm i --force
cd ..
  1. 设置科尔多瓦:

    ¥Set up Cordova:

cd SheetJSIonic
ionic cordova plugin add cordova-plugin-file
ionic cordova platform add ios --confirm
ionic cordova platform add android --confirm
npm i --save @awesome-cordova-plugins/core @awesome-cordova-plugins/file @ionic/cordova-builders

如果在平台之前添加 cordova-plugin-file,安装可能会失败:

¥If cordova-plugin-file is added before the platforms, installation may fail:

CordovaError: Could not load API for ios project

这可以通过删除并重新安装 ios 平台来解决:

¥This can be resolved by removing and reinstalling the ios platform:

ionic cordova platform rm ios
ionic cordova platform add ios --confirm

如果 npm i 步骤由于 rxjs 解析而失败,请将高亮的行添加到 package.json 以强制解析:

¥If the npm i step fails due to rxjs resolution, add the highlighted lines to package.json to force a resolution:

package.json
  "private": true,
"overrides": {
"rxjs": "~7.5.0"
},
"dependencies": {

请注意,错误日志中将显示所需的 rxjs 版本。

¥Note that the required rxjs version will be displayed in the error log.

添加这些行后,npm i 命令将成功。

¥After adding the lines, the npm i command will succeed.

  1. 安装依赖:

    ¥Install dependencies:

npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
  1. @awesome-cordova-plugins/file 添加到模块中。下面高亮差异:

    ¥Add @awesome-cordova-plugins/file to the module. Differences highlighted below:

src/app/app.module.ts
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { File } from '@awesome-cordova-plugins/file/ngx';

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],

providers: [File, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
bootstrap: [AppComponent],
})
export class AppModule {}
  1. 下载 home.page.ts 并替换:

    ¥Download home.page.ts and replace:

curl -o src/app/home/home.page.ts -L https://xlsx.nodejs.cn/ionic/home.page.ts

iOS

  1. 启用文件共享并使文档文件夹在 iOS 应用中可见。将以下行添加到 platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist

    ¥Enable file sharing and make the documents folder visible in the iOS app. Add the following lines to platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist:

platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist (add to file)
<plist version="1.0">
<dict>
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>CFBundleDevelopmentRegion</key>

(文档的根元素是 plist,它包含一个 dict 子元素)

¥(The root element of the document is plist and it contains one dict child)

  1. 构建应用并启动模拟器

    ¥Build the app and start the simulator

ionic cordova emulate ios

加载应用时,应显示总统列表。该列表是通过获取和解析测试文件动态生成的。

¥When the app is loaded, a list of Presidents should be displayed. This list is dynamically generated by fetching and parsing a test file.

在某些测试运行中,cordova build ios --emulator 步骤失败并出现错误:

¥In some test runs, the cordova build ios --emulator step failed with error:

> cordova build ios --emulator
Could not load API for ios project

通过强制安装 cordova-ios 解决了这个问题:

¥This was resolved by forcefully installing cordova-ios:

npm i --save cordova-ios

在最近的测试中,native-run ios 命令失败并显示

¥In the most recent test, the native-run ios command failed with

[native-run] ERR_UNKNOWN: Path 'platforms/ios/build/emulator/SheetJSIonic.app' not found

检查 platforms/ios/build/,实际文件夹名称是:

¥Inspecting platforms/ios/build/, the actual folder name was:

% ls platforms/ios/build
Debug-iphonesimulator

iOS 模拟器可以手动启动:

¥The iOS simulator can be launched manually:

native-run ios --app platforms/ios/build/Debug-iphonesimulator/SheetJSIonic.app --virtual

在某些测试中,emulate 命令失败并显示:

¥In some tests, the emulate command failed with:

Error: Unknown argument: platform
[ERROR] An error occurred while running subprocess ng.

ng run app:ionic-cordova-build --platform=ios exited with exit code 1.

修复方法是手动添加 @ionic/cordova-builders

¥The fix is to manually add @ionic/cordova-builders:

ng add @ionic/cordova-builders

安卓

¥Android

  1. 在 Android 应用中启用文件读写。

    ¥Enable file reading and writing in the Android app.

编辑 platforms/android/app/src/main/AndroidManifest.xml 并在 application 标记之前添加以下两行:

¥Edit platforms/android/app/src/main/AndroidManifest.xml and add the following two lines before the application tag:

platforms/android/app/src/main/AndroidManifest.xml (add to file)
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

application 标签中,添加属性 android:requestLegacyExternalStorage="true"

¥In the application tag, add the attribute android:requestLegacyExternalStorage="true".

  1. 构建应用并启动模拟器

    ¥Build the app and start the emulator

ionic cordova emulate android

加载应用时,应显示总统列表。该列表是通过获取和解析测试文件动态生成的。

¥When the app is loaded, a list of Presidents should be displayed. This list is dynamically generated by fetching and parsing a test file.

在某些测试运行中,cordova build android --emulator 步骤失败并出现错误:

¥In some test runs, cordova build android --emulator step failed with error:

Could not find or parse valid build output file

通过强制安装 cordova-android 解决了这个问题:

¥This was resolved by forcefully installing cordova-android:

npm i --save cordova-android

在某些测试中,构建失败并出现 Gradle 错误:

¥In some tests, the build failed with a Gradle error:

Could not find an installed version of Gradle either in Android Studio,
or on your system to install the gradle wrapper. Please include gradle
in your path or install Android Studio

在 macOS 上,通过使用 Homebrew 管理器安装 gradle 解决了此问题:

¥On macOS, this issue was resolved by installing gradle with Homebrew manager:

brew install gradle

上次在 Android 上测试演示时,读取文件按预期工作。但是,生成的文件在“文件”应用中无法从外部看到。

¥When the demo was last tested on Android, reading files worked as expected. However, the generated files were not externally visible from the Files app.

这是 Android SDK 33 和底层文件插件的一个已知错误!

¥This is a known bug with Android SDK 33 and the underlying file plugins!

[^1]: 见 API 参考中的 "数组的数组"

¥See "Array of Arrays" in the API reference

[^2]: 请参阅 Ionic 文档中的 ion-grid

¥See ion-grid in the Ionic documentation.

[^3]: 请参阅 Angular 文档中的 ngFor

¥See ngFor in the Angular documentation.

[^4]: 见 read 于 "读取文件"

¥See read in "Reading Files"

[^5]: 见 "数组输出" 于 "实用函数"

¥See "Array Output" in "Utility Functions"

[^6]: 见 aoa_to_sheet 于 "实用工具"

¥See aoa_to_sheet in "Utilities"

[^7]: 有关 book_newbook_append_sheet 的详细信息,请参阅 "工作簿助手" 于 "实用工具"

¥See "Workbook Helpers" in "Utilities" for details on book_new and book_append_sheet.

[^8]: 见 write 于 "写入文件"

¥See write in "Writing Files"

[^9]: 查看 "为 iOS 开发""为 Android 开发"。Ionic 团队从官方文档站点中删除了这些页面,并推荐 vercel.app 文档站点。

¥See "Developing for iOS" and "Developing for Android". The Ionic team removed these pages from the official docs site and recommend the vercel.app docs site.

[^10]: 请参阅 JDK 存档 for Java 17 JDK 下载链接。

¥See the JDK Archive for Java 17 JDK download links.