📋 系统概述
本系统实现 良田(ELoam)高拍仪 的Web拍照与自动上传功能。通过浏览器即可操作高拍仪拍照,照片自动上传到配置的阿里云服务器。
核心能力:
- 通过WebSocket调用良田SDK(eloamwebservice)控制高拍仪
- 支持 自动纠偏(SetDeskew) — 拍照后自动检测试卷/文档边界并拉正
- 照片上传到阿里云服务器,文件名包含操作人姓名
- 所有服务器连接配置(地址、端口、用户名、密码)可通过Web界面动态修改
适用场景: 试卷扫描、文档归档、证件拍照上传等需要高拍仪配合云存储的场景。
✅ 验证结果
| 功能 | 状态 | 说明 |
|---|---|---|
| SDK WebSocket连接 | ✓ 通过 | eloamwebservice 127.0.0.1:9000 |
| 设备初始化 InitDevs | ✓ 通过 | 自动处理 ret=2 (Init again) 重试 |
| 打开摄像头 OpenCamera | ✓ 通过 | 需 datacallback:true |
| 拍照 ScanImage | ✓ 通过 | base64模式,约400KB/张 |
| 自动纠偏 SetDeskew | ✓ 通过 | ret=0,拍照前设置 |
| 上传至阿里云 | ✓ 通过 | FTP协议连接阿里云服务器 |
| HTTP服务端口3001 | ✓ 通过 | Express + 静态文件 |
🏗️ 系统架构图
浏览器 → Node.js服务器 → eloamwebservice → 高拍仪(拍照),Node.js → 阿里云服务器(上传)
🔧 技术栈
后端
- Node.js v22
- Express 4.18
- ws 8.21 (WebSocket客户端)
- basic-ftp 5.0
- multer (文件上传)
前端
- 原生HTML + CSS + JavaScript
- 无框架依赖
- Fetch API
- 响应式设计
外部依赖
- eloamwebservice.exe (SDK服务)
- Windows USB驱动
- 阿里云服务器 (FTP服务端)
硬件
- 良田高拍仪 S1280AF
- 3264×2448 最大分辨率
- USB 连接
🔌 API 接口文档
所有接口返回 JSON 格式。基础地址:http://localhost:3001
| 方法 | 路径 | 说明 | 关键参数 |
|---|---|---|---|
GET | /capture | 拍照 | deskew=1 启用自动纠偏 |
POST | /upload | 上传Base64图片到阿里云 | image, name, filename |
POST | /upload-file | 上传文件到阿里云 | file (multipart) |
GET | /config | 获取阿里云连接配置 | - |
POST | /config | 修改阿里云连接配置 | host, port, user, password |
GET | /test-ftp | 测试阿里云连接 | - |
GET | /health | 健康检查 | - |
📸 GET /capture 拍照
调用高拍仪SDK完成拍照,返回Base64格式JPEG图片。
GET http://localhost:3001/capture?deskew=1
参数:
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
deskew | string | 0 | 1 或 true 启用自动纠偏 |
响应示例:
{
"success": true,
"data": "data:image/jpeg;base64,/9j/4AAQ...",
"size": 243461,
"deskew": true,
"time": "2026-06-11T10:52:34.959Z"
}
📤 POST /upload 上传图片
将Base64图片数据上传到阿里云服务器(通过FTP协议)。文件名自动包含操作人姓名。
POST http://localhost:3001/upload
Content-Type: application/json
{
"image": "data:image/jpeg;base64,/9j/4AAQ...",
"name": "张三",
"filename": "张三_1718000000000.jpg"
}
响应示例:
{
"success": true,
"message": "上传成功",
"path": "张三_1718000000000.jpg",
"size": 243461,
"name": "张三",
"url": "ftp://阿里云地址/张三_1718000000000.jpg"
}
📁 项目文件结构
所有源码在 C:\良田高拍仪v8.1\ftp-upload\ 目录下:
| 文件 | 大小 | 说明 |
|---|---|---|
server.js |
~11KB | 主服务程序 — Express HTTP服务 + WebSocket相机控制 + 阿里云上传,含SetDeskew自动纠偏 |
index.html |
~13KB | 用户界面 — 阿里云配置、拍照控制、图片预览、上传操作、自动纠偏开关 |
package.json |
<1KB | Node.js依赖声明(express, ws, basic-ftp, multer) |
ftp_config.json |
<1KB | 运行时生成的阿里云连接配置持久化文件 |
test-upload.js |
~2KB | 上传功能测试脚本 |
README.md |
~3KB | 说明文档 |
docs.html |
本页 | 交互式技术文档 |
node_modules/ |
自动生成 | npm依赖包(无需手动修改) |
📄 server.js 核心模块
server.js 包含以下功能模块:
| 模块 | 行数 | 功能 |
|---|---|---|
| 阿里云配置管理 | 14-43 | 加载/保存 ftp_config.json |
| 上传函数 | 66-92 | uploadToFtp() — 通过FTP协议上传文件到阿里云 |
| 相机拍照函数 | 94-202 | captureFromCamera() — WebSocket控制SDK完成拍照 |
| 拍照接口 | 204-218 | GET /capture?deskew=0|1 |
| 配置接口 | 220-241 | GET|POST /config |
| 上传接口 | 243-283 | POST /upload 和 POST /upload-file |
| 测试接口 | 285-314 | GET /test-ftp 和 GET /health |
| 文件查找接口 | 316-339 | GET /find-capture(备用) |
📄 index.html 界面布局
| 区域 | 功能 |
|---|---|
| 阿里云服务器配置 | 主机地址、端口、用户名、密码输入 + 保存/测试按钮 |
| 操作人信息 | 姓名输入(文件名中使用),保存到localStorage |
| 服务状态 | 显示服务器健康状态指示灯 |
| 拍照控制 | 拍照按钮 + 自动上传开关 + 自动纠偏开关 |
| 图片预览 | 拍照后显示图片,上传/清除按钮 |
| 操作日志 | 实时显示所有操作记录 |
| 进度覆盖层 | 上传时显示进度条 |
🚀 安装与启动
前置条件: 已安装 eloamwebservice(良田SDK服务),摄像头已通过USB连接。
📦 安装依赖
cd C:\良田高拍仪v8.1\ftp-upload npm install
会自动安装 express、ws、basic-ftp、multer。
▶️ 启动服务
方式一:前台运行(推荐)
cd C:\良田高拍仪v8.1\ftp-upload npm start
看到启动横幅后打开浏览器访问 http://localhost:3001
方式二:后台运行(隐藏窗口)
Start-Process -FilePath "node" -ArgumentList "server.js" -WorkingDirectory "C:\良田高拍仪v8.1\ftp-upload" -WindowStyle Hidden
📋 启动日志说明
已加载保存的FTP配置 ╔══════════════════════════════════════════════════════════╗ ║ 高拍仪上传服务已启动(阿里云) ║ ╠══════════════════════════════════════════════════════════╣ ║ 服务地址: http://localhost:3001 ║ 阿里云服务器: 已配置 ║ 监控文件夹: C:\良田高拍仪v8.1\Scans ╠══════════════════════════════════════════════════════════╣ ║ [文件监控] (启动时自动运行) ║ ║ POST /watcher/stop - 停止监控 ║ ║ GET|POST /watcher/config - 监控配置 ║ ║ GET /watcher/history - 上传历史记录 ║ ╠══════════════════════════════════════════════════════════╣ ║ [相机控制] ║ ║ GET /capture - 拍照 (调用高拍仪SDK) ║ ║ POST /upload - Base64图片上传 (支持name参数) ║ ╠══════════════════════════════════════════════════════════╣ ║ [系统配置] ║ ║ GET|POST /config - 阿里云连接配置 ║ ║ GET /test-ftp - 测试阿里云连接 ║ ║ GET /health - 健康检查 ║ ╚══════════════════════════════════════════════════════════╝ [2026-06-11T10:51:28.238Z] 服务已就绪,文件监控已自动启动
🔍 验证服务
# 健康检查
curl http://localhost:3001/health
# 返回: {"status":"ok","time":"2026-06-11T10:00:00.000Z"}
# 测试阿里云连接
curl http://localhost:3001/test-ftp
# 返回: {"success":true,"message":"连接成功","config":{...}}
📖 使用指南
打开浏览器访问 http://localhost:3001,界面包含以下操作区域:
① 配置阿里云服务器
填写阿里云服务器地址、FTP端口、用户名、密码,点击 "保存阿里云配置"。配置会自动保存到 ftp_config.json,下次启动自动加载。
点击 "测试连接" 验证阿里云是否可达。
② 输入操作人姓名
填写姓名后点击保存。上传到阿里云的文件名会自动包含该姓名,例如 张三_1718000000000.jpg。
③ 拍照
点击 "拍照" 按钮,服务端自动完成以下流程:
连接SDK → 初始化设备 → 设置纠偏(可选) → 打开摄像头 → 等待3秒 → 拍照 → 返回图片
整个过程约 5~6秒。拍照完成后图片自动显示在预览区域。
勾选 "启用自动纠偏" 后,SDK会自动检测试卷/文档边界并拉正。
④ 上传到阿里云
拍照后手动点击 "上传到阿里云" 上传。
或勾选 "拍照后自动上传到阿里云",拍照完成后自动上传。
⑤ 查看日志
底部日志区域实时显示所有操作记录,包括SDK通信状态和上传结果。
🔄 SDK交互流程图
每次拍照(GET /capture)触发以下完整WebSocket交互序列:
📡 WebSocket消息序列
📝 关键代码:captureFromCamera()
function captureFromCamera(options = {}) {
return new Promise((resolve, reject) => {
// 1. 连接WebSocket到 127.0.0.1:9000
// 2. InitDevs → 若 ret=2 则 DeinitDevs 后重试
// 3. (可选) SetDeskew(isdeskew:1) → ret=0
// 4. OpenCamera(device:0, resolution:0, datacallback:true)
// 5. 等待 3 秒
// 6. ScanImage(imagepath:'', colorize:0, type:true, quality:80)
// 7. 返回 base64 JPEG 数据
// 8. 清理: CloseCamera → DeinitDevs → ws.close()
});
}
📋 SDK消息示例
发送: {"function":"InitDevs"}
收到: {"function":"InitDevs","ret":2,"value":"Init again"}
发送: {"function":"DeinitDevs"}
收到: {"function":"DeinitDevs","ret":0,"value":"success"}
发送: {"function":"InitDevs"}
收到: {"function":"InitDevs","ret":0,"value":"success"}
发送: {"function":"SetDeskew","isdeskew":1}
收到: {"function":"SetDeskew","ret":0,"value":"success"}
发送: {"function":"OpenCamera","device":0,"resolution":0,"datacallback":true}
收到: {"function":"OpenCamera","ret":0,"value":"success"}
# 等待 3 秒
发送: {"function":"ScanImage","imagepath":"","colorize":0,"type":true,"quality":80}
收到: {"function":"ScanImage","ret":0,"value":"/9j/4AAQSkZJRgABAQEA... (base64数据)"}
📤 上传至阿里云流程
使用 basic-ftp 库通过FTP协议上传到阿里云服务器。阿里云上需开启FTP服务(如 vsftpd 或 Windows FTP服务端)。
📝 上传流程
浏览器 → POST /upload {image, name, filename}
→ 服务端解码base64为Buffer
→ 构建文件名: {姓名}_{时间戳}.jpg
→ 连接阿里云FTP: client.access({host, port, user, password})
→ 上传: client.uploadFrom(stream, filename)
→ 返回: {success, path, size, url}
🔧 阿里云连接配置
通过Web界面配置阿里云FTP连接信息,保存到 ftp_config.json:
{
"host": "阿里云公网IP",
"port": 12121,
"user": "ftp用户名",
"password": "ftp密码"
}
⚠ 密码明文存储在本地文件,建议阿里云上为每个学校创建独立FTP账号以控制权限。
📄 文件名规则
| 场景 | 文件名示例 |
|---|---|
| 有操作人姓名 | 张三_1718000000000.jpg |
| 无操作人姓名 | eloam_1718000000000.jpg |
| 自定义文件名 | 以请求中 filename 为准 |
❓ 常见问题
🔴 打开摄像头失败 ret=-3
原因: 摄像头被其他进程独占(如 EloamCamera.exe)。
解决:
# 查找并杀掉占用摄像头的进程 taskkill /F /IM EloamCamera.exe # 重启eloamwebservice taskkill /F /IM eloamwebservice.exe # 重新启动服务
🔴 InitDevs 返回 ret=2 "Init again"
说明: SDK已经初始化,需要先反初始化再重新初始化。
处理: 系统会自动发送 DeinitDevs 后重试 InitDevs。
🔴 拍照返回 ret=-2
原因: ScanImage 参数不兼容。base64 模式(imagepath:'')在某些SDK版本中不可用。
解决: 已经调通 base64 模式,如果再次出现可切换为文件路径模式(指定 imagepath 为文件路径后再读取)。
🔴 无法连接 eloamwebservice
原因: SDK服务未启动或端口9000被占用。
解决:
# 检查服务是否运行 netstat -ano | findstr ":9000" # 启动服务(以管理员身份) "C:\Program Files (x86)\eloamwebservice\bin\eloamwebservice.exe"
🔴 无法连接到阿里云服务器
- 检查阿里云服务器是否运行中、FTP服务是否已启动
- 在Web界面重新配置正确的地址/端口/用户名/密码
- 检查阿里云安全组是否放行了FTP端口
- 使用
GET /test-ftp测试连接
🔴 端口3001被占用
# 查看哪个进程占用3001 netstat -ano | findstr ":3001" # 杀掉进程 taskkill /F /PID <进程ID>
⚠️ 文件监控模式注意事项
① 多人共用电脑需独立文件夹
多位老师共用一台电脑时(各自 Windows 账号登录),监控文件夹必须按用户隔离:
C:\Users\教师A\Pictures\高拍仪扫描\ C:\Users\教师B\Pictures\高拍仪扫描\
每个老师各自启动 start.bat,各自监控自己的目录。
② EloamCamera 保存路径需手动设置
打开 EloamCamera.exe → 设置/配置 → 保存路径 → 改成上述监控文件夹。
否则图片保存到默认位置,不会被检测到上传。
⚠️ 安全风险与注意事项
① 阿里云密码明文存储风险
FTP密码保存在本机 ftp_config.json 文件中,为明文形式。任何能使用这台电脑的人都可以用记事本打开查看密码。
建议:阿里云上为每个学校创建独立的FTP账号,密码泄露时只需吊销单个账号。
② 文件监控无上传失败提示
文件监控模式下,如果上传失败(如网络断开、阿里云FTP服务宕机),用户不会收到任何弹窗或通知。失败记录仅在网页历史记录中可查,但用户可能不会主动去查看。
建议:定期检查上传历史记录,或观察文件是否堆积在监控文件夹中未被删除。
③ 重复启动风险
多次双击 start.bat 会启动多个 Node.js 进程,导致端口3001冲突或重复监控。
建议:启动前先运行 stop.bat 确保旧进程已终止。
④ 上传后误删源文件
如果勾选了"上传后删除源文件",但上传实际是失败的(如FTP返回假成功),源文件会丢失。
建议:谨慎使用"上传后删除"选项,建议先观察一段时间确认上传可靠后再启用。