- App Inventor 2 云数据库配置教程 - Firebase/云数据库1/自建后台
本文 PHP 后端已配套 Node.js 与 Python 版本,见页面中的对应实现;全部后端源码与运行说明。
App Inventor 2 云数据库配置教程 - Firebase/云数据库1/自建后台
在App Inventor 2中,如果你的应用需要在多台设备之间共享数据,或者需要将数据持久化存储在云端,就需要使用云数据库组件。本文将全面介绍App Inventor 2提供的云存储方案,并给出详细的配置步骤和实践代码。
一、云存储方案对比
App Inventor 2内置了多种云端数据存储组件,每种方案各有适用场景:
| 特性 | 云数据库1 | Firebase数据库1 | 网络微数据库1(自建后台) | Google Spreadsheet |
|---|---|---|---|---|
| 底层技术 | Redis服务器 | Google Firebase Realtime DB | 自定义Web服务API | Google Sheets API |
| 默认服务器 | MIT提供默认服务器 | 需自行配置Firebase项目 | 需自行搭建 | 需配置Google账户 |
| 实时同步 | ✅ 支持DataChanged事件 | ✅ 实时数据监听 | ❌ 需手动轮询 | ❌ 需手动轮询 |
| 数据共享 | ✅ 多用户共享 | ✅ 多用户共享 | ✅ 多用户共享 | ✅ 多用户共享 |
| 离线支持 | ❌ 需联网 | ✅ 本地缓存+同步 | ❌ 需联网 | ❌ 需联网 |
| 列表操作 | ✅ 原子追加/移除 | ✅ 支持 | ❌ 需自行实现 | ✅ 读写行列 |
| 安全规则 | Token认证 | Firebase Rules | 自行实现 | Google权限管理 |
| 适用场景 | 通用云端存储、实时协作 | 实时聊天、排行榜 | 完全自定义业务逻辑 | 数据报表、表单收集 |
| 难度 | ⭐ 简单 | ⭐⭐ 中等 | ⭐⭐⭐ 较高 | ⭐⭐ 中等 |
| 费用 | MIT免费(有限额) | Firebase免费额度 | 服务器自费 | Google免费额度 |
需要用户账号、图片等文件、按条件查询时,上面几种都很吃力,直接用 BaaS 拓展:国内用 CloudBase(腾讯云开发),海外或不想实名认证用 Supabase。Firebase 和 Google Spreadsheet 在国内访问不稳定。
二、云数据库1 —— MIT官方云数据库
2.1 简介
CloudDB是App Inventor 2内置的非可视化组件,基于Redis服务器实现。它允许应用用户之间共享数据。默认情况下,数据存储在MIT维护的服务器上,你也可以搭建自己的Redis服务器。
核心特点:
- 开箱即用,默认配置即可直接使用
- 支持实时数据变更通知(DataChanged事件)
- 原子性列表操作,多设备并发安全
- 支持自建Redis服务器
2.2 配置步骤(使用MIT默认服务器)
步骤1:添加组件
在App Inventor 2设计视图中,从左侧组件面板的「Storage」分类中拖拽 云数据库1 组件到屏幕上。
步骤2:配置属性
| 属性 | 设置值 | 说明 |
|---|---|---|
RedisServer |
DEFAULT |
使用MIT默认服务器 |
RedisPort |
6381 |
默认端口,无需修改 |
Token |
不修改 | 系统自动填充认证令牌 |
UseSSL |
true |
默认服务器必须开启SSL |
ProjectID |
自动生成 | 每个项目唯一标识 |
提示: 使用MIT默认服务器时,所有属性保持默认即可,无需手动配置。
2.3 配置步骤(使用自建Redis服务器)
如果你需要完全掌控数据,可以搭建自己的Redis服务器:
步骤1:安装Redis
# Ubuntu/Debian
sudo apt update
sudo apt install redis-server
# CentOS
sudo yum install redis
# Docker方式(推荐)
docker run -d --name redis-clouddb \
-p 6381:6379 \
-v redis-data:/data \
redis:7 redis-server --appendonly yes --requirepass your_password_here
步骤2:配置Redis
编辑Redis配置文件(通常为 /etc/redis/redis.conf):
# 设置监听端口
port 6381
# 设置密码
requirepass your_secure_password
# 开启持久化
appendonly yes
appendfilename "appendonly.aof"
# 绑定地址(根据需要调整)
bind 0.0.0.0
步骤3:在App Inventor中配置
| 属性 | 设置值 | 说明 |
|---|---|---|
RedisServer |
你的服务器IP或域名 |
如 192.168.1.100 或 redis.example.com |
RedisPort |
6381 |
与服务器端口一致 |
Token |
your_secure_password |
Redis服务器密码 |
UseSSL |
根据服务器配置 | 如果服务器启用了SSL则为true |
ProjectID |
自定义 | 多项目可使用不同ProjectID隔离数据 |
2.4 数据读写积木块操作
存储数据
云数据库1.StoreValue("userName", "张三")
当存储完成后,会触发 云数据库1.UpdateDone(tag, operation) 事件。
读取数据
云数据库1.GetValue("userName", "")
when 云数据库1.GotValue(tag, value) {
if tag == "userName" {
标签1.Text = value
}
}
注意:
GetValue是异步操作,不会直接返回值。你必须在GotValue事件中处理返回的数据。
监听数据变化
when 云数据库1.DataChanged(tag, value) {
// 当任何设备修改了数据,所有连接的设备都会收到此事件
标签_状态.Text = join("数据已更新:", tag, " = ", value)
}
列表操作
// 原子性追加元素到列表(多设备并发安全)
云数据库1.AppendValueToList("chatMessages", "你好!")
// 原子性获取并移除列表首元素(适合消息队列)
云数据库1.RemoveFirstFromList("taskQueue")
when 云数据库1.FirstRemoved(value) {
标签_结果.Text = join("取出:", value)
}
获取所有标签
云数据库1.GetTagList()
when 云数据库1.TagList(value) {
// value 是所有标签名的列表
列表选择器1.Elements = value
}
删除数据
// 删除指定标签的数据
云数据库1.ClearTag("userName")
// 检查连接状态
标签_状态.Text = 云数据库1.CloudConnected()
2.5 完整示例:简易聊天应用
界面设计:
- TextBox_Chat:输入聊天消息
- 按钮_发送:发送按钮
- Label_Messages:显示聊天记录
积木块逻辑:
// Screen初始化
when Screen1.Initialize() {
云数据库1.GetValue("chatHistory", makeList())
}
// 发送消息
when 按钮_发送.Click() {
云数据库1.AppendValueToList(
"chatHistory",
TextBox_Chat.Text
)
TextBox_Chat.Text = ""
}
// 收到历史消息
when 云数据库1.GotValue(tag, value) {
if tag == "chatHistory" {
Label_Messages.Text = join(value)
}
}
// 实时监听新消息
when 云数据库1.DataChanged(tag, value) {
if tag == "chatHistory" {
Label_Messages.Text = join(value)
}
}
三、Firebase数据库1 —— Google实时数据库
3.1 简介
FirebaseDB组件对接Google Firebase Realtime Database,提供实时数据同步能力。数据以JSON树形结构存储,支持离线缓存和自动同步。
核心特点:
- Google级别的稳定性和扩展性
- 真正的实时数据同步
- 离线缓存,网络恢复后自动同步
- 丰富的安全规则配置
- 免费额度充足(Spark计划)
3.2 Firebase项目创建
步骤1:注册Firebase
- 访问 Firebase控制台
- 使用Google账户登录
- 点击「添加项目」
- 输入项目名称(如
my-appinventor-app) - 选择是否启用Google Analytics(可选)
- 点击「创建项目」
步骤2:创建Realtime Database
- 在Firebase控制台左侧菜单中找到「Build」→「Realtime Database」
- 点击「Create Database」
- 选择数据库位置(建议选择离用户最近的区域)
- 选择安全规则模式:
- 测试模式:允许所有读写(开发阶段使用)
- 锁定模式:拒绝所有读写(生产环境推荐)
步骤3:获取配置信息
在Firebase项目设置中记录以下信息:
- 数据库URL:格式为
https://your-project-id-default-rtdb.firebaseio.com/ - Firebase Token:在Realtime Database的规则中配置
3.3 在App Inventor中配置FirebaseDB
步骤1:添加组件
在设计视图中,从「Storage」分类拖拽 Firebase数据库1 组件到屏幕。
步骤2:配置属性
| 属性 | 设置值 | 说明 |
|---|---|---|
FirebaseToken |
从Firebase获取的Token | 项目设置中的Web API Key |
FirebaseURL |
数据库URL | https://xxx.firebaseio.com/ |
UseService |
false |
使用Token认证方式 |
3.4 Firebase安全规则配置
在Firebase控制台的「Realtime Database」→「Rules」中配置安全规则:
开发/测试阶段规则(允许所有读写):
{
"rules": {
".read": true,
".write": true
}
}
生产环境推荐规则(需要认证):
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
"public": {
".read": true,
".write": "auth !== null"
}
}
}
⚠️ 安全警告: 测试模式规则会在30天后自动过期。生产环境务必配置适当的安全规则,避免数据泄露。
3.5 数据读写积木块操作
存储数据
Firebase数据库1.StoreValue("score", 100)
when Firebase数据库1.DataChanged(tag, value) {
// 数据存储成功或变更时触发
}
读取数据
Firebase数据库1.GetValue("score", 0)
when Firebase数据库1.GotValue(tag, value) {
if tag == "score" {
标签_分数.Text = join("分数:", value)
}
}
删除数据
Firebase数据库1.ClearTag("score")
追加到列表
Firebase数据库1.AppendValue("messages", "新消息")
移除列表首元素
Firebase数据库1.RemoveFirst("taskQueue")
when Firebase数据库1.FirstRemoved(value) {
标签_结果.Text = value
}
3.6 完整示例:实时排行榜
界面设计:
- 文本输入框_名称:输入玩家名
- 文本输入框_分数:输入分数
- Button_Submit:提交按钮
- Label_Leaderboard:显示排行榜
积木块逻辑:
// 初始化加载排行榜
when Screen1.Initialize() {
Firebase数据库1.GetValue("leaderboard", makeList())
}
// 提交分数
when Button_Submit.Click() {
var entry = join(文本输入框_名称.Text, ":", 文本输入框_分数.Text)
Firebase数据库1.AppendValue(
"leaderboard",
entry
)
}
// 收到数据
when Firebase数据库1.GotValue(tag, value) {
if tag == "leaderboard" {
Label_Leaderboard.Text = join(value)
}
}
// 实时更新
when Firebase数据库1.DataChanged(tag, value) {
if tag == "leaderboard" {
Label_Leaderboard.Text = join(value)
}
}
四、网络微数据库1 —— 自建网络微数据库后台
4.1 简介
TinyWebDB是App Inventor 2中最灵活的云存储方案。它不绑定特定的云服务商,而是通过HTTP接口与你自己搭建的Web服务器通信。你可以使用PHP、Node.js、Python等任何后端技术来实现服务接口。
从下载源码到手机读写成功的完整步骤(自己电脑、云服务器两条路线),见《网络微数据库后台搭建教程》。
核心特点:
- 完全自主可控,数据存储在自己的服务器
- 可以使用MySQL、SQLite、MongoDB等任何数据库
- 可以实现复杂的业务逻辑(用户认证、数据过滤等)
- 可以部署在国内服务器(阿里云、腾讯云等),访问速度快
- 支持PHP、Node.js、Python、Java等后端技术
4.2 网络微数据库1 API规范
TinyWebDB组件与后台服务之间通过标准HTTP POST请求通信,使用特定的JSON格式:
保存数据(storeavalue)
请求:
POST /api/storeavalue
Content-Type: application/x-www-form-urlencoded
tag=userName&value="张三"
value 是值的 JSON 文本(文本带引号,列表是 [...]),实际发送时会再做表单编码。服务端原样保存即可。
成功响应(返回 HTTP 200 即可,内容不检查):
["STORED", "userName", "\"张三\""]
获取数据(getvalue)
请求:
POST /api/getvalue
Content-Type: application/x-www-form-urlencoded
tag=userName
成功响应(数据存在):
["VALUE", "userName", "\"张三\""]
第三项放回保存时收到的 JSON 文本,组件会把它解析回原来的值。
成功响应(数据不存在):
["VALUE", "userName", ""]
4.3 在App Inventor中配置TinyWebDB
步骤1:添加组件
在设计视图中,从「Storage」分类拖拽 网络微数据库1 组件到屏幕。
步骤2:配置属性
| 属性 | 设置值 | 说明 |
|---|---|---|
ServiceURL |
http://你的服务器地址/api |
你的后台服务URL |
注意: ServiceURL 末尾不要加
/,组件会自动拼成服务地址/storeavalue;多写一个/会变成//storeavalue,下面的 Node.js 示例就匹配不上。如果使用HTTPS,确保服务器SSL证书有效。
4.4 PHP后台实现方案
方案一:基于文件的简易存储
以下PHP代码使用JSON文件存储数据,适合小型应用:
请求路径是 /tinywebdb/api/storeavalue、/tinywebdb/api/getvalue,需要让它们都交给 index.php 处理:Apache 在 api/ 目录放一个 .htaccess,写一行 FallbackResource /tinywebdb/api/index.php;Nginx 用 try_files $uri /tinywebdb/api/index.php;。
文件结构:
/var/www/html/tinywebdb/
├── api/
│ ├── index.php # API入口
│ └── data/
│ └── store.json # 数据文件
api/index.php:
<?php
/**
* 网络微数据库1 API - 文件存储版
* 适用于小型应用,无需数据库
*/
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$dataFile = __DIR__ . '/data/store.json';
// 确保数据目录存在
if (!is_dir(__DIR__ . '/data')) {
mkdir(__DIR__ . '/data', 0755, true);
}
// 读取现有数据
function loadData($file) {
if (file_exists($file)) {
$content = file_get_contents($file);
return json_decode($content, true) ?: [];
}
return [];
}
// 保存数据
function saveData($file, $data) {
file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
// 获取POST参数
$tag = isset($_POST['tag']) ? $_POST['tag'] : '';
$value = isset($_POST['value']) ? $_POST['value'] : '';
// 路由处理
$action = basename($_SERVER['REQUEST_URI']);
if ($action === 'storeavalue') {
// 保存数据
if (empty($tag)) {
echo json_encode(["ERROR", "Tag cannot be empty"]);
exit;
}
$data = loadData($dataFile);
$data[$tag] = $value;
saveData($dataFile, $data);
echo json_encode(["STORED", $tag, $value]);
} elseif ($action === 'getvalue') {
// 获取数据
if (empty($tag)) {
echo json_encode(["ERROR", "Tag cannot be empty"]);
exit;
}
$data = loadData($dataFile);
$resultValue = isset($data[$tag]) ? $data[$tag] : "";
echo json_encode(["VALUE", $tag, $resultValue]);
} else {
echo json_encode(["ERROR", "Unknown action: $action"]);
}
?>
方案二:基于MySQL的完整方案
适合生产环境,支持高并发:
数据库初始化(SQL):
CREATE DATABASE IF NOT EXISTS tinywebdb
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;
USE tinywebdb;
CREATE TABLE IF NOT EXISTS store (
id INT AUTO_INCREMENT PRIMARY KEY,
tag VARCHAR(255) NOT NULL,
value TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY idx_tag (tag)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
api/index.php:
<?php
/**
* 网络微数据库1 API - MySQL存储版
* 适用于生产环境
*/
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
// 数据库配置
$dbHost = 'localhost';
$dbName = 'tinywebdb';
$dbUser = 'tinywebdb_user';
$dbPass = 'your_secure_password';
// 可选:简易API密钥认证
$apiKey = 'your_api_key_here';
$enableAuth = false; // 设为 true 启用认证
// 认证检查
if ($enableAuth) {
$requestKey = isset($_SERVER['HTTP_X_API_KEY']) ? $_SERVER['HTTP_X_API_KEY'] : '';
if ($requestKey !== $apiKey) {
http_response_code(401);
echo json_encode(["ERROR", "Unauthorized"]);
exit;
}
}
// 数据库连接
try {
$pdo = new PDO(
"mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4",
$dbUser,
$dbPass,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
} catch (PDOException $e) {
echo json_encode(["ERROR", "Database connection failed"]);
exit;
}
// 获取POST参数
$tag = isset($_POST['tag']) ? $_POST['tag'] : '';
$value = isset($_POST['value']) ? $_POST['value'] : '';
// 路由处理
$action = basename($_SERVER['REQUEST_URI']);
if ($action === 'storeavalue') {
if (empty($tag)) {
echo json_encode(["ERROR", "Tag cannot be empty"]);
exit;
}
// 使用 INSERT ... ON DUPLICATE KEY UPDATE 实现 upsert
$stmt = $pdo->prepare(
"INSERT INTO store (tag, value) VALUES (:tag, :value)
ON DUPLICATE KEY UPDATE value = :value2, updated_at = NOW()"
);
$stmt->execute([
':tag' => $tag,
':value' => $value,
':value2'=> $value
]);
echo json_encode(["STORED", $tag, $value]);
} elseif ($action === 'getvalue') {
if (empty($tag)) {
echo json_encode(["ERROR", "Tag cannot be empty"]);
exit;
}
$stmt = $pdo->prepare("SELECT value FROM store WHERE tag = :tag");
$stmt->execute([':tag' => $tag]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$resultValue = $row ? $row['value'] : "";
echo json_encode(["VALUE", $tag, $resultValue]);
} else {
echo json_encode(["ERROR", "Unknown action: $action"]);
}
?>
MySQL 存储的 Node.js 与 Python 版本
使用上面的同一份建库建表 SQL,接口路径、字段和响应数组与 PHP 版相同。两个版本都使用参数化 SQL,并利用 tag 的唯一索引更新已有值。数据库配置用环境变量:
export DB_HOST=127.0.0.1 DB_PORT=3306 DB_NAME=tinywebdb
export DB_USER=tinywebdb_user DB_PASS=你的数据库密码
# 可选:设置后启用 X-API-Key 认证;不设置时与 PHP 的 enableAuth=false 一致
export API_KEY=你的API密钥
用 Web 客户端访问时将 X-API-Key 放进请求头。网络微数据库没有设置自定义请求头的属性,因此启用该认证后请通过 Web 客户端发送表单请求。启动后 ServiceURL 为 http://服务器IP:3000/tinywebdb/api,末尾不加 /。
Node.js 对应实现
保存为 tinywebdb-mysql.cjs,在文件所在目录安装依赖并启动:
npm install express@5 mysql2
node tinywebdb-mysql.cjs
// npm install express@5 mysql2
const express = require('express');
const mysql = require('mysql2/promise');
const app = express();
const pool = mysql.createPool({host: process.env.DB_HOST || '127.0.0.1', port: Number(process.env.DB_PORT || 3306),
user: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME || 'tinywebdb', charset: 'utf8mb4'});
app.use(express.urlencoded({extended: false}));
app.use((req, res, next) => { res.set('Access-Control-Allow-Origin', '*'); next(); });
app.post(['/api/:action', '/tinywebdb/api/:action'], async (req, res) => {
// 设置 API_KEY 启用认证;客户端发送 X-API-Key 请求头。
if (process.env.API_KEY && req.get('X-API-Key') !== process.env.API_KEY) return res.status(401).json(['ERROR', 'Unauthorized']);
const {tag = '', value = ''} = req.body || {};
if (!tag) return res.json(['ERROR', 'Tag cannot be empty']);
try {
if (req.params.action === 'storeavalue') {
await pool.execute('INSERT INTO store (tag, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = ?, updated_at = NOW()', [tag, value, value]);
return res.json(['STORED', tag, value]);
}
if (req.params.action === 'getvalue') {
const [rows] = await pool.execute('SELECT value FROM store WHERE tag = ?', [tag]);
return res.json(['VALUE', tag, rows.length ? rows[0].value : '']);
}
res.json(['ERROR', 'Unknown action: ' + req.params.action]);
} catch (error) { res.json(['ERROR', 'Database operation failed']); }
});
if (require.main === module) app.listen(Number(process.env.PORT || 3000), '0.0.0.0');
module.exports = {app, pool};
Python 对应实现
保存为 tinywebdb_mysql.py,在文件所在目录安装依赖并启动:
python3 -m pip install Flask PyMySQL
python3 tinywebdb_mysql.py
# python3 -m pip install Flask PyMySQL
import os
import pymysql
from flask import Flask, request, jsonify
app = Flask(__name__)
def connect():
return pymysql.connect(host=os.environ.get('DB_HOST', '127.0.0.1'), port=int(os.environ.get('DB_PORT', 3306)),
user=os.environ['DB_USER'], password=os.environ['DB_PASS'], database=os.environ.get('DB_NAME', 'tinywebdb'),
charset='utf8mb4', autocommit=True)
@app.after_request
def cors(response):
response.headers['Access-Control-Allow-Origin'] = '*'
return response
@app.post('/api/<action>')
@app.post('/tinywebdb/api/<action>')
def api(action):
if os.environ.get('API_KEY') and request.headers.get('X-API-Key') != os.environ['API_KEY']:
return jsonify(['ERROR', 'Unauthorized']), 401
tag, value = request.form.get('tag', ''), request.form.get('value', '')
if not tag: return jsonify(['ERROR', 'Tag cannot be empty'])
if action not in ('storeavalue', 'getvalue'): return jsonify(['ERROR', 'Unknown action: ' + action])
connection = None
try:
connection = connect()
with connection.cursor() as cursor:
if action == 'storeavalue':
cursor.execute('INSERT INTO store (tag, value) VALUES (%s, %s) ON DUPLICATE KEY UPDATE value = %s, updated_at = NOW()', (tag, value, value))
return jsonify(['STORED', tag, value])
cursor.execute('SELECT value FROM store WHERE tag = %s', (tag,))
row = cursor.fetchone()
return jsonify(['VALUE', tag, row[0] if row else ''])
except (pymysql.MySQLError, KeyError):
return jsonify(['ERROR', 'Database operation failed'])
finally:
if connection is not None: connection.close()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 3000)))
4.5 Node.js 与 Python 文件存储方案
下面两个版本对应 4.4 的 PHP 文件存储版。请求是表单 POST,字段为 tag、value;value 是组件序列化后的 JSON 文本,服务端原样保存,因此字符串、数字、列表和字典都能往返。成功响应分别为 ["STORED", tag, value]、["VALUE", tag, value],不存在的 tag 返回空文本。
启动后设置 ServiceURL = http://服务器IP:3000/tinywebdb/api,末尾不加 /。也兼容原 Node.js 示例的 /api 路径。数据默认保存在脚本旁 data/store.json,可用 DATA_FILE 更改。文件版适合单进程教学,跨进程或多台服务器写入请用 4.4 的 MySQL 版。
Node.js 对应实现
保存为 tinywebdb-file.cjs,在文件所在目录安装依赖并启动:
npm install express@5
node tinywebdb-file.cjs
// npm install express@5
const express = require('express');
const fs = require('node:fs');
const path = require('node:path');
const app = express();
const file = path.resolve(process.env.DATA_FILE || path.join(__dirname, 'data', 'store.json'));
fs.mkdirSync(path.dirname(file), {recursive: true});
app.use(express.urlencoded({extended: false}));
app.use((req, res, next) => { res.set('Access-Control-Allow-Origin', '*'); next(); });
// 同时保留 PHP 的路径和原 Node.js 示例的 /api 路径。
app.post(['/api/:action', '/tinywebdb/api/:action'], (req, res) => {
const {tag = '', value = ''} = req.body || {};
if (!tag) return res.json(['ERROR', 'Tag cannot be empty']);
// 同步读写在单个 Node.js 进程内串行执行;多进程请使用数据库版。
const data = Object.assign(Object.create(null), fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : {});
if (req.params.action === 'storeavalue') {
data[tag] = value; // value 是 JSON 文本,不在服务端再次解析
fs.writeFileSync(file + '.tmp', JSON.stringify(data), 'utf8');
fs.renameSync(file + '.tmp', file);
return res.json(['STORED', tag, value]);
}
if (req.params.action === 'getvalue') return res.json(['VALUE', tag, Object.hasOwn(data, tag) ? data[tag] : '']);
res.json(['ERROR', 'Unknown action: ' + req.params.action]);
});
app.use((err, req, res, next) => res.status(500).json(['ERROR', 'Storage failed']));
if (require.main === module) app.listen(Number(process.env.PORT || 3000), '0.0.0.0');
module.exports = app;
Python 对应实现
保存为 tinywebdb_file.py,在文件所在目录安装依赖并启动:
python3 -m pip install Flask
python3 tinywebdb_file.py
# python3 -m pip install Flask
import json
import os
import threading
from pathlib import Path
from flask import Flask, request, jsonify
app = Flask(__name__)
FILE = Path(os.environ.get('DATA_FILE', Path(__file__).parent / 'data' / 'store.json'))
FILE.parent.mkdir(parents=True, exist_ok=True)
LOCK = threading.Lock() # 单进程多线程;多进程部署请使用数据库版
@app.after_request
def cors(response):
response.headers['Access-Control-Allow-Origin'] = '*'
return response
@app.post('/api/<action>')
@app.post('/tinywebdb/api/<action>')
def api(action):
tag, value = request.form.get('tag', ''), request.form.get('value', '')
if not tag: return jsonify(['ERROR', 'Tag cannot be empty'])
try:
with LOCK:
data = json.loads(FILE.read_text(encoding='utf-8')) if FILE.exists() else {}
if action == 'storeavalue':
data[tag] = value # value 是 JSON 文本,原样存取
temporary = FILE.with_name(FILE.name + '.tmp')
temporary.write_text(json.dumps(data, ensure_ascii=False), encoding='utf-8')
temporary.replace(FILE)
return jsonify(['STORED', tag, value])
if action == 'getvalue': return jsonify(['VALUE', tag, data.get(tag, '')])
return jsonify(['ERROR', 'Unknown action: ' + action])
except (OSError, ValueError):
return jsonify(['ERROR', 'Storage failed']), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 3000)))
4.6 TinyWebDB数据读写积木块操作
存储数据
网络微数据库1.StoreValue("userName", "张三")
when 网络微数据库1.ValueStored() {
// 数据存储成功
标签_状态.Text = "保存成功"
}
读取数据
网络微数据库1.GetValue("userName")
when 网络微数据库1.GotValue(tag, valueFromWebDB) {
if tag == "userName" {
标签_名称.Text = valueFromWebDB
}
}
注意: 与CloudDB/FirebaseDB类似,
GetValue是异步操作,必须在GotValue事件中处理返回数据。
错误处理
when 网络微数据库1.WebServiceError(message) {
标签_错误.Text = join("网络错误:", message)
}
4.7 完整示例:用户登录注册系统
界面设计:
- TextBox_Username:用户名输入
- TextBox_Password:密码输入
- Button_Register:注册按钮
- Button_Login:登录按钮
- 标签_状态:状态提示
积木块逻辑:
注册和登录都要先 GetValue,结果回到同一个 GotValue 事件里,所以用全局变量 mode 记住这次读取是为了注册还是登录。
global mode = ""
when Button_Register.Click() {
if TextBox_Username.Text == "" or TextBox_Password.Text == "" {
标签_状态.Text = "用户名和密码不能为空"
} else {
mode = "注册"
网络微数据库1.GetValue(join("user:", TextBox_Username.Text))
}
}
when Button_Login.Click() {
mode = "登录"
网络微数据库1.GetValue(join("user:", TextBox_Username.Text))
}
when 网络微数据库1.GotValue(tagFromWebDB, valueFromWebDB) {
if mode == "注册" {
if valueFromWebDB == "" {
网络微数据库1.StoreValue(tagFromWebDB, TextBox_Password.Text)
标签_状态.Text = "注册成功!"
} else {
标签_状态.Text = "用户名已存在"
}
} else if mode == "登录" {
if valueFromWebDB == "" {
标签_状态.Text = "用户名不存在"
} else if valueFromWebDB == TextBox_Password.Text {
标签_状态.Text = "登录成功!"
} else {
标签_状态.Text = "密码错误"
}
}
mode = ""
}
这个示例把密码明文存在网络微数据库里,任何知道服务地址的人都能读到,只适合学习原理。正式的账号系统请用 CloudBase 或 Supabase 的认证组件,详见注册登录教程。
五、Web组件 —— 通用HTTP数据交互
除了以上专门的数据库组件,App Inventor 2还提供了 Web 组件,可以与任何HTTP API进行数据交互,适合与自建 PHP/Node.js/Python 服务器进行复杂数据交换。
5.1 发送GET请求
// 设置请求URL
Web客户端1.Url = "http://你的服务器/api/getData.php?key=value"
Web客户端1.Get()
when Web客户端1.GotText(responseCode, responseContent) {
if responseCode == 200 {
标签_结果.Text = responseContent
}
}
5.2 发送POST请求
// 设置请求URL和参数
Web客户端1.Url = "http://你的服务器/api/saveData.php"
Web客户端1.RequestHeaders = list(list("Content-Type", "application/x-www-form-urlencoded"))
Web客户端1.PostText(join("name=", Web客户端1.UriEncode(文本输入框_名称.Text), "&score=", Web客户端1.UriEncode(文本输入框_分数.Text)))
when Web客户端1.GotText(responseCode, responseContent) {
if responseCode == 200 {
标签_状态.Text = "提交成功"
}
}
5.3 PHP后台接收示例
<?php
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
$key = isset($_GET['key']) ? $_GET['key'] : '';
// 处理GET请求,查询数据
$result = ["status" => "ok", "data" => "查询结果"];
echo json_encode($result, JSON_UNESCAPED_UNICODE);
} elseif ($method === 'POST') {
$name = isset($_POST['name']) ? $_POST['name'] : '';
$score = isset($_POST['score']) ? $_POST['score'] : '';
// 处理POST请求,保存数据
$result = ["status" => "ok", "message" => "保存成功"];
echo json_encode($result, JSON_UNESCAPED_UNICODE);
}
?>
5.4 Node.js 与 Python 接收示例
下面对应 5.3 的 PHP 示例,保留 /api/getData.php?key=… 和 /api/saveData.php 路径。POST 使用表单字段 name、score,返回同样的 JSON。将网址中的主机改成 服务器IP:3000 即可。示例只展示参数读取和响应,查询、保存业务请写在注释标记的位置。
Node.js 对应实现
保存为 web-data.cjs,在文件所在目录安装依赖并启动:
npm install express@5
node web-data.cjs
// npm install express@5
const express = require('express');
const app = express();
app.use(express.urlencoded({extended: false}));
app.use((req, res, next) => { res.set('Access-Control-Allow-Origin', '*'); next(); });
app.get('/api/getData.php', (req, res) => {
const key = req.query.key || ''; // 在这里按 key 查询自己的数据
res.json({status: 'ok', data: '查询结果'});
});
app.post('/api/saveData.php', (req, res) => {
const {name = '', score = ''} = req.body || {}; // 在这里校验并保存 name、score
res.json({status: 'ok', message: '保存成功'});
});
if (require.main === module) app.listen(Number(process.env.PORT || 3000), '0.0.0.0');
module.exports = app;
Python 对应实现
保存为 web_data.py,在文件所在目录安装依赖并启动:
python3 -m pip install Flask
python3 web_data.py
# python3 -m pip install Flask
import os
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.after_request
def cors(response):
response.headers['Access-Control-Allow-Origin'] = '*'
return response
@app.get('/api/getData.php')
def get_data():
key = request.args.get('key', '') # 在这里按 key 查询自己的数据
return jsonify(status='ok', data='查询结果')
@app.post('/api/saveData.php')
def save_data():
name, score = request.form.get('name', ''), request.form.get('score', '')
# 在这里校验并保存 name、score
return jsonify(status='ok', message='保存成功')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 3000)))
六、安全最佳实践
6.1 数据传输安全
| 措施 | 说明 |
|---|---|
| 使用HTTPS | 生产环境务必启用SSL证书(Let’s Encrypt免费获取) |
| API密钥认证 | 自建后台应实现API Key验证 |
| 输入验证 | 后台必须验证和过滤所有输入数据 |
| 速率限制 | 防止恶意请求,建议每IP每分钟限制请求数 |
6.2 Firebase安全规则进阶
{
"rules": {
// 公开只读数据
"public": {
".read": true,
".write": false
},
// 已认证用户可读写
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
},
// 限流:每分钟最多10次写入
"messages": {
".read": true,
".write": "auth != null && newData.exists()",
"$messageId": {
".validate": "newData.hasChildren(['text', 'timestamp'])"
}
}
}
}
6.3 自建后台安全加固
Nginx反向代理配置示例:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
# 限流
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
location /api/ {
limit_req zone=api burst=10 nodelay;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
七、方案选择建议
根据不同的应用场景,推荐以下选择策略:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 课堂演示、快速原型 | 网络微数据库1(国内免费服务)或 云数据库1(默认服务器) | 零搭建;默认服务器为共享测试用途,数据可能被清理 |
| 实时聊天/协作 | 云数据库1 | DataChanged 事件实时推送 |
| 排行榜/成绩统计 | 云数据库1 | 原子列表操作,多设备并发安全 |
| 用户注册登录 | CloudBase / Supabase | 内建账号体系,密码不经过你的数据库 |
| 图片、文件上传与展示 | CloudBase / Supabase 的存储组件 | 上传后拿 URL 直接给图像组件显示 |
| 按条件查询、排序、分页 | CloudBase / Supabase 的数据库组件 | 网络微数据库只能按标签取值 |
| 国内用户为主、要自主可控 | 自建后台(网络微数据库协议 或 Web组件 + MySQL) | 部署在国内服务器,访问速度快 |
| 需要复杂业务逻辑 | Web组件 + 自建后台,或 CloudBase 云函数 | 逻辑放在服务端 |
八、常见问题(FAQ)
Q1:CloudDB的MIT默认服务器有容量限制吗?
MIT默认服务器是共享资源,适合开发和测试。对于生产环境或高并发应用,建议自建Redis服务器。
Q2:Firebase在国内能正常访问吗?
Firebase服务在国内访问可能不稳定。如果目标用户在国内,需要账号、数据库和文件存储一整套能力时用 CloudBase;只存简单数据用 CloudDB(自建Redis)或 TinyWebDB(自建后台)。Supabase 国内可以访问,速度偏慢,可作为 Firebase 的平替。
Q3:TinyWebDB的ServiceURL可以配置多个吗?
每个TinyWebDB组件只能配置一个ServiceURL。如果需要连接多个服务,可以添加多个TinyWebDB组件。
Q4:如何在不同应用之间共享CloudDB数据?
CloudDB的数据隔离基于ProjectID。要跨应用共享数据,可以使用相同的Token值。将一个应用的Token复制到另一个应用的CloudDB组件中即可实现数据共享。
Q5:自建后台可以用云服务器吗?
可以。阿里云ECS、腾讯云CVM、华为云ECS等国内云服务器都可以部署TinyWebDB后台。推荐使用Ubuntu系统,搭配Nginx反向代理和Let’s Encrypt免费SSL证书。
九、参考资源
© 2024 App Inventor 2 中文网。本文基于MIT官方文档编写,仅供学习参考。
扫码添加客服咨询