主要功能:

  • 上传图片,自动识别文字

  • 支持中英文混合识别

  •  一键复制识别结果

技术栈:

  • 前端:HTML + CSS + JavaScript

  • 后端:Node.js + Express

  • API:百度OCR文字识别

一、项目架构设计

┌─────────────┐
│   前端页面   │ (ocr_baidu_demo.html)
└──────┬──────┘
       │ ① 上传图片
       ↓
┌─────────────┐
│  后端服务器  │ (ocr_server.js)
└──────┬──────┘
       │ ② 调用API
       ↓
┌─────────────┐
│  百度OCR API │
└──────┬──────┘
       │ ③ 返回结果
       ↓
┌─────────────┐
│   显示文字   │
└─────────────┘

二、准备工作

步骤一:注册账号

  1. 访问百度AI开放平台:https://ai.baidu.com/

  2. 点击右上角「注册/登录」

步骤二:创建应用

  1. 进入「控制台」

  2. 选择「产品服务」→「文字识别」

  3. 点击「创建应用」

  4. 填写应用名称和描述

  5. 提交审核(通常秒过)

步骤三:获取密钥

创建成功后,你会得到:

  • API Key:类似 AbCdEfGh1234567890

  • Secret Key:类似 XyZaBcDeFg0987654321

三、项目搭建

3.1 项目目录结构

baidu-ocr-demo/
├── ocr_baidu_demo.html    # 前端页面
├── ocr_server.js           # 后端服务器
├── package.json            # 项目配置
└── README.md               # 说明文档

3.2 初始化项目

在终端中创建项目文件夹:

mkdir baidu-ocr-demo
cd baidu-ocr-demo

创建package.json:

{
  "name": "baidu-ocr-demo",
  "version": "1.0.0",
  "description": "百度OCR图片文字识别工具",
  "main": "ocr_server.js",
  "scripts": {
    "start": "node ocr_server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "cors": "^2.8.5",
    "axios": "^1.6.0"
  }
}

安装依赖:

npm install

四、node开发

4.1 创建服务器(ocr_server.js)

完整代码如下:

/**
 * 百度OCR后端服务器
 * 功能:接收前端上传的图片,调用百度OCR API进行识别
 */

const express = require('express');
const cors = require('cors');
const axios = require('axios');

const app = express();
const PORT = 3000;

// ========== 配置百度API密钥 ==========
// ⚠️ 请替换为你自己的密钥
const BAIDU_API_KEY = '你的API_KEY';
const BAIDU_SECRET_KEY = '你的SECRET_KEY';

// ========== 中间件配置 ==========
app.use(cors());  // 允许跨域
app.use(express.json({ limit: '10mb' }));  // 解析JSON,限制10MB

// ========== 全局变量 ==========
let accessToken = null;      // 访问令牌
let tokenExpireTime = 0;     // 令牌过期时间

/**
 * 获取百度API访问令牌
 * 令牌有效期30天,会自动缓存
 */
async function getAccessToken() {
    // 如果令牌存在且未过期,直接返回
    if (accessToken && Date.now() < tokenExpireTime) {
        return accessToken;
    }
    
    try {
        console.log('🔑 正在获取访问令牌...');
        
        // 调用百度OAuth API
        const response = await axios.get(
            'https://aip.baidubce.com/oauth/2.0/token',
            {
                params: {
                    grant_type: 'client_credentials',
                    client_id: BAIDU_API_KEY,
                    client_secret: BAIDU_SECRET_KEY
                }
            }
        );
        
        // 保存令牌(提前5分钟过期,避免边界问题)
        accessToken = response.data.access_token;
        tokenExpireTime = Date.now() + (response.data.expires_in - 300) * 1000;
        
        console.log('✅ 令牌获取成功');
        return accessToken;
        
    } catch (error) {
        console.error('❌ 获取令牌失败:', error.message);
        throw new Error('获取百度API令牌失败');
    }
}

/**
 * 调用百度OCR API识别文字
 */
async function recognizeText(imageBase64) {
    try {
        const token = await getAccessToken();
        
        console.log('🔍 正在识别文字...');
        
        // 调用百度OCR API
        const response = await axios.post(
            'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic',
            `image=${encodeURIComponent(imageBase64)}`,
            {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                params: {
                    access_token: token
                }
            }
        );
        
        console.log('✅ 识别完成');
        return response.data;
        
    } catch (error) {
        console.error('❌ 识别失败:', error.message);
        throw new Error('百度OCR识别失败');
    }
}

// ========== API路由 ==========

/**
 * POST /ocr - 文字识别接口
 */
app.post('/ocr', async (req, res) => {
    try {
        const { image } = req.body;
        
        // 验证参数
        if (!image) {
            return res.status(400).json({ error: '缺少图片数据' });
        }
        
        // 检查是否配置密钥
        if (BAIDU_API_KEY === '你的API_KEY') {
            return res.status(500).json({ 
                error: '请先配置百度API密钥' 
            });
        }
        
        console.log('📥 收到识别请求');
        
        // 调用识别服务
        const result = await recognizeText(image);
        
        // 返回结果
        res.json(result);
        
    } catch (error) {
        console.error('❌ 处理失败:', error);
        res.status(500).json({ error: error.message });
    }
});

/**
 * GET / - 服务状态
 */
app.get('/', (req, res) => {
    res.json({
        status: 'running',
        message: '百度OCR服务器运行中',
        version: '1.0.0'
    });
});

// ========== 启动服务器 ==========
app.listen(PORT, () => {
    console.log('================================');
    console.log('🚀 百度OCR服务器启动成功!');
    console.log(`📡 服务地址:http://localhost:${PORT}`);
    console.log('================================');
});

关键点1:获取访问令牌

async function getAccessToken() {
    // 缓存机制:避免频繁请求
    if (accessToken && Date.now() < tokenExpireTime) {
        return accessToken;
    }
    
    // 调用OAuth API获取令牌
    const response = await axios.get(
        'https://aip.baidubce.com/oauth/2.0/token',
        {
            params: {
                grant_type: 'client_credentials',
                client_id: BAIDU_API_KEY,
                client_secret: BAIDU_SECRET_KEY
            }
        }
    );
    
    return response.data.access_token;
}

关键点2:调用OCR API

const response = await axios.post(
    'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic',
    `image=${encodeURIComponent(imageBase64)}`,  // URL编码
    {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        params: {
            access_token: token  // 令牌通过query参数传递
        }
    }
);

五、前端开发

5.1 创建页面(ocr_baidu_demo.html)

完整代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>百度OCR文字识别</title>
    <style>
        /* 基础样式 */
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        
        h1 {
            text-align: center;
            color: #333;
        }
        
        /* 提示框 */
        .info {
            background: #e3f2fd;
            padding: 15px;
            margin: 20px 0;
            border-radius: 5px;
            font-size: 14px;
        }
        
        /* 上传区域 */
        .upload-box {
            border: 2px dashed #4CAF50;
            padding: 40px;
            text-align: center;
            margin: 20px 0;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s;
        }
        
        .upload-box:hover {
            background-color: #f0f0f0;
        }
        
        /* 按钮 */
        .btn {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            margin: 5px;
            transition: all 0.3s;
        }
        
        .btn:hover {
            background-color: #45a049;
        }
        
        /* 加载动画 */
        #loading {
            display: none;
            text-align: center;
            margin: 20px 0;
        }
        
        .spinner {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #4CAF50;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
            margin: 0 auto;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        
        /* 结果显示 */
        #result {
            display: none;
            margin-top: 20px;
        }
        
        #previewImg {
            max-width: 100%;
            max-height: 300px;
            margin: 10px 0;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        
        #textOutput {
            width: 100%;
            min-height: 150px;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            margin: 10px 0;
            font-size: 14px;
            line-height: 1.6;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>百度OCR文字识别</h1>
        
        <!-- 提示信息 -->
        <div class="info">
            ⚠️ 使用前请先启动后端服务:<code>node ocr_server.js</code>
        </div>
        
        <!-- 上传区域 -->
        <div class="upload-box" id="uploadBox" 
             onclick="document.getElementById('fileInput').click()">
            <p>点击选择图片</p>
            <p style="color: #999; font-size: 14px;">
                支持 JPG、PNG、BMP 格式,大小不超过 4MB
            </p>
            <input type="file" id="fileInput" accept="image/*" style="display:none;">
        </div>
        
        <!-- 加载中 -->
        <div id="loading">
            <div class="spinner"></div>
            <p>正在识别中,请稍候...</p>
        </div>
        
        <!-- 结果显示 -->
        <div id="result">
            <h3>识别结果:</h3>
            <img id="previewImg" src="" alt="预览图">
            <textarea id="textOutput" readonly></textarea>
            <div style="text-align: center;">
                <button class="btn" onclick="copyText()">复制文字</button>
                <button class="btn" onclick="downloadText()">下载文本</button>
                <button class="btn" onclick="reset()">重新识别</button>
            </div>
        </div>
    </div>
    
    <script>
        // ========== 全局变量 ==========
        let currentText = '';
        const API_URL = 'http://localhost:3000/ocr';
        
        // ========== 文件选择事件 ==========
        document.getElementById('fileInput').addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (file) handleFile(file);
        });
        
        /**
         * 处理上传的图片
         */
        async function handleFile(file) {
            // 1. 验证文件
            if (!file.type.startsWith('image/')) {
                alert('❌ 请选择图片文件!');
                return;
            }
            
            if (file.size > 4 * 1024 * 1024) {
                alert('❌ 图片大小不能超过 4MB!');
                return;
            }
            
            // 2. 切换界面状态
            document.getElementById('uploadBox').style.display = 'none';
            document.getElementById('loading').style.display = 'block';
            document.getElementById('result').style.display = 'none';
            
            try {
                // 3. 显示预览图
                const reader = new FileReader();
                reader.onload = (e) => {
                    document.getElementById('previewImg').src = e.target.result;
                };
                reader.readAsDataURL(file);
                
                // 4. 转换为Base64
                const base64Image = await fileToBase64(file);
                const base64Data = base64Image.split(',')[1];
                
                // 5. 调用后端API
                const response = await fetch(API_URL, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ image: base64Data })
                });
                
                if (!response.ok) {
                    throw new Error('API请求失败');
                }
                
                const result = await response.json();
                
                if (result.error) {
                    throw new Error(result.error);
                }
                
                // 6. 显示结果
                displayResult(result);
                
            } catch (error) {
                alert('❌ 识别失败:' + error.message + 
                      '\n\n请确保后端服务已启动!');
                reset();
            }
        }
        
        /**
         * 文件转Base64编码
         */
        function fileToBase64(file) {
            return new Promise((resolve, reject) => {
                const reader = new FileReader();
                reader.onload = () => resolve(reader.result);
                reader.onerror = reject;
                reader.readAsDataURL(file);
            });
        }
        
        /**
         * 显示识别结果
         */
        function displayResult(result) {
            // 提取所有文字
            const words = result.words_result || [];
            currentText = words.map(item => item.words).join('\n');
            
            // 显示文字
            document.getElementById('textOutput').value = 
                currentText || '未识别到文字';
            
            // 切换显示
            document.getElementById('loading').style.display = 'none';
            document.getElementById('result').style.display = 'block';
        }
        
        /**
         * 复制文字到剪贴板
         */
        function copyText() {
            if (!currentText) {
                alert('❌ 没有可复制的内容');
                return;
            }
            
            // 使用现代API
            navigator.clipboard.writeText(currentText).then(() => {
                alert('✅ 已复制到剪贴板!');
            }).catch(() => {
                // 降级方案
                const textarea = document.getElementById('textOutput');
                textarea.select();
                document.execCommand('copy');
                alert('✅ 已复制到剪贴板!');
            });
        }
        
        /**
         * 下载为txt文件
         */
        function downloadText() {
            if (!currentText) {
                alert('❌ 没有可下载的内容');
                return;
            }
            
            const blob = new Blob([currentText], { 
                type: 'text/plain;charset=utf-8' 
            });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'ocr_result_' + Date.now() + '.txt';
            a.click();
            URL.revokeObjectURL(url);
            
            alert('✅ 文件已下载!');
        }
        
        /**
         * 重置界面
         */
        function reset() {
            document.getElementById('uploadBox').style.display = 'block';
            document.getElementById('loading').style.display = 'none';
            document.getElementById('result').style.display = 'none';
            document.getElementById('fileInput').value = '';
            currentText = '';
        }
    </script>
</body>
</html>

六、运行测试
 

6.1 配置API密钥

编辑 ocr_server.js,填入你的密钥:

const BAIDU_API_KEY = 'your_api_key_here';        // 替换这里
const BAIDU_SECRET_KEY = 'your_secret_key_here';  // 替换这里

6.2 启动后端服务

在项目目录下执行:

node ocr_server.js

看到以下提示表示成功:

================================
🚀 百度OCR服务器启动成功!
📡 服务地址:http://localhost:3000
================================

6.3 打开前端页面

  1. 双击打开 ocr_baidu_demo.html

  2. 效果展示

        

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐