feature: 借助拦截器,对接百度OCR,并通过拦截配置提供OCR接口。
parent
81dc4a545b
commit
86d2077553
|
@ -0,0 +1,188 @@
|
||||||
|
function getTomorrow () {
|
||||||
|
const now = new Date()
|
||||||
|
const tomorrow = new Date(now)
|
||||||
|
|
||||||
|
// 设置日期为明天
|
||||||
|
tomorrow.setDate(now.getDate() + 1)
|
||||||
|
// 重置时间为凌晨 0 点 0 分 0 秒
|
||||||
|
tomorrow.setHours(0, 0, 0, 0)
|
||||||
|
|
||||||
|
return tomorrow.getTime()
|
||||||
|
}
|
||||||
|
// function getNextMonth () {
|
||||||
|
// const now = new Date()
|
||||||
|
// const currentYear = now.getFullYear()
|
||||||
|
// const currentMonth = now.getMonth()
|
||||||
|
//
|
||||||
|
// // 如果当前月份是12月,年份增加1,并且月份设为0(1月)
|
||||||
|
// const nextMonth = (currentMonth + 1) % 12
|
||||||
|
// const nextYear = nextMonth === 0 ? currentYear + 1 : currentYear
|
||||||
|
//
|
||||||
|
// return new Date(nextYear, nextMonth, 1, 0, 0, 0, 0).getTime()
|
||||||
|
// }
|
||||||
|
|
||||||
|
const AipOcrClient = require('baidu-aip-sdk').ocr
|
||||||
|
const AipOcrClientMap = {}
|
||||||
|
const apis = [
|
||||||
|
'accurateBasic', // 调用通用文字识别(高精度版)
|
||||||
|
'accurate', // 调用通用文字识别(含位置高精度版)
|
||||||
|
'handwriting' // 手写文字识别
|
||||||
|
]
|
||||||
|
|
||||||
|
const limitMap = {}
|
||||||
|
|
||||||
|
function createBaiduOcrClient (config) {
|
||||||
|
const key = config.id
|
||||||
|
if (AipOcrClientMap[key]) {
|
||||||
|
return AipOcrClientMap[key]
|
||||||
|
}
|
||||||
|
const client = new AipOcrClient(config.id, config.ak, config.sk)
|
||||||
|
AipOcrClientMap[key] = client
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
function getConfig (interceptOpt, tryCount, log) {
|
||||||
|
tryCount = tryCount || 1
|
||||||
|
|
||||||
|
let config
|
||||||
|
if (typeof (interceptOpt.baiduOcr) && interceptOpt.baiduOcr.length > 0) {
|
||||||
|
config = interceptOpt.baiduOcr[count++ % interceptOpt.baiduOcr.length]
|
||||||
|
|
||||||
|
if (tryCount < interceptOpt.baiduOcr.length) {
|
||||||
|
if (!config || !config.id || !config.ak || !config.sk) {
|
||||||
|
return getConfig(interceptOpt, tryCount + 1, log) // 递归找到有效的配置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 避免count值过大,造成问题
|
||||||
|
if (count >= 100000) count = 0
|
||||||
|
} else {
|
||||||
|
config = interceptOpt.baiduOcr
|
||||||
|
tryCount = null // 将tryCount设置为null代表只有一个配置
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config || !config.id || !config.ak || !config.sk) {
|
||||||
|
return null // 没有配置或配置错误,直接返回null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前配置可用的API
|
||||||
|
for (let i = 0; i < apis.length; i++) {
|
||||||
|
const api = apis[i]
|
||||||
|
if (!checkIsLimitConfig(config.id, api)) {
|
||||||
|
config.api = api
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.warn(`百度云账号 ${config.id} 的接口 ${api} 已超出限额`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果当前配置的所有API均不可用,则返回null
|
||||||
|
if (config.api == null) {
|
||||||
|
if (tryCount == null) {
|
||||||
|
return null // 只配置了一个账号,没有更多账号可以选择了,直接返回null
|
||||||
|
} else {
|
||||||
|
if (tryCount < interceptOpt.baiduOcr.length) {
|
||||||
|
// 递归找到有效的配置
|
||||||
|
return getConfig(interceptOpt, tryCount + 1, log)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
function limitConfig (id, api) {
|
||||||
|
const key = id + '_' + api
|
||||||
|
limitMap[key] = getTomorrow()
|
||||||
|
// limitMap[key] = Date.now() + 5000 // 测试用,5秒后解禁
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIsLimitConfig (id, api) {
|
||||||
|
const key = id + '_' + api
|
||||||
|
const limitTime = limitMap[key]
|
||||||
|
return limitTime && limitTime > Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'baiduOcr',
|
||||||
|
priority: 131,
|
||||||
|
requestIntercept (context, interceptOpt, req, res, ssl, next, matched) {
|
||||||
|
const { rOptions, log } = context
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
'Access-Control-Allow-Origin': '*'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取配置
|
||||||
|
const config = getConfig(interceptOpt, null, log)
|
||||||
|
if (!config) {
|
||||||
|
res.writeHead(200, headers)
|
||||||
|
res.write('{"error_code": 99917, "error_msg": "dev-sidecar中,未配置百度云账号,或所有百度云账号的免费额度都已用完!!!"}')
|
||||||
|
res.end()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!config.id || !config.ak || !config.sk) {
|
||||||
|
res.writeHead(200, headers)
|
||||||
|
res.write('{"error_code": 999500, "error_msg": "dev-sidecar中,baiduOcr的 id 或 ak 或 sk 配置为空"}')
|
||||||
|
res.end()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
headers['DS-Interceptor'] = `baiduOcr: id=${config.id}, api=${config.api || apis[0]}, account=${config.account}`
|
||||||
|
|
||||||
|
// 获取图片的base64编码
|
||||||
|
let imageBase64 = rOptions.path.substring(rOptions.path.indexOf('?') + 1)
|
||||||
|
if (!imageBase64) {
|
||||||
|
res.writeHead(200, headers)
|
||||||
|
res.write('{"error_code": 999400, "error_msg": "图片Base64参数为空"}')
|
||||||
|
res.end()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
imageBase64 = decodeURIComponent(imageBase64)
|
||||||
|
|
||||||
|
// 调用百度云 “文字识别” 相关接口,根据 `config.api` 调用不同的接口
|
||||||
|
const client = createBaiduOcrClient(config)
|
||||||
|
const options = {
|
||||||
|
recognize_granularity: 'big',
|
||||||
|
detect_direction: 'false',
|
||||||
|
paragraph: 'false',
|
||||||
|
probability: 'false',
|
||||||
|
...(config.options || {})
|
||||||
|
}
|
||||||
|
log.info('发起百度ocr请求', req.hostname)
|
||||||
|
client[config.api || apis[0]](imageBase64, options).then(function (result) {
|
||||||
|
if (result.error_code != null) {
|
||||||
|
log.error('baiduOcr error:', result)
|
||||||
|
if (result.error_code === 17) {
|
||||||
|
// 当前百度云账号,达到当日调用次数上限
|
||||||
|
limitConfig(config.id, config.api)
|
||||||
|
log.error(`当前百度云账号的接口 ${config.api},已达到当日调用次数上限,暂时禁用它,明天会自动放开:`, config)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info('baiduOcr success:', result)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(200, headers)
|
||||||
|
res.write(JSON.stringify(result)) // 格式如:{"words_result":[{"words":"6525"}],"words_result_num":1,"log_id":1818877093747960000}
|
||||||
|
res.end()
|
||||||
|
if (next) next() // 异步执行完继续next
|
||||||
|
}).catch(function (err) {
|
||||||
|
log.info('baiduOcr error:', err)
|
||||||
|
res.writeHead(200, headers)
|
||||||
|
res.write('{"error_code": 999500, "error_msg": "' + err + '"}') // 格式如:{"words_result":[{"words":"6525"}],"words_result_num":1,"log_id":1818877093747960000}
|
||||||
|
res.end()
|
||||||
|
if (next) next() // 异步执行完继续next
|
||||||
|
})
|
||||||
|
|
||||||
|
log.info('proxy baiduOcr: hostname:', req.hostname)
|
||||||
|
|
||||||
|
return 'no-next'
|
||||||
|
},
|
||||||
|
is (interceptOpt) {
|
||||||
|
return !!interceptOpt.baiduOcr
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,8 @@ const requestReplace = require('./impl/req/requestReplace')
|
||||||
const proxy = require('./impl/req/proxy')
|
const proxy = require('./impl/req/proxy')
|
||||||
const sni = require('./impl/req/sni')
|
const sni = require('./impl/req/sni')
|
||||||
|
|
||||||
|
const baiduOcr = require('./impl/req/baiduOcr')
|
||||||
|
|
||||||
// response interceptor impls
|
// response interceptor impls
|
||||||
const OPTIONSHeaders = require('./impl/res/AfterOPTIONSHeaders')
|
const OPTIONSHeaders = require('./impl/res/AfterOPTIONSHeaders')
|
||||||
const cacheRes = require('./impl/res/cacheRes')
|
const cacheRes = require('./impl/res/cacheRes')
|
||||||
|
@ -24,6 +26,7 @@ module.exports = [
|
||||||
success, redirect, abort, cacheReq,
|
success, redirect, abort, cacheReq,
|
||||||
requestReplace,
|
requestReplace,
|
||||||
proxy, sni,
|
proxy, sni,
|
||||||
|
baiduOcr,
|
||||||
|
|
||||||
// response interceptor impls
|
// response interceptor impls
|
||||||
OPTIONSHeaders, cacheRes, responseReplace,
|
OPTIONSHeaders, cacheRes, responseReplace,
|
||||||
|
|
Loading…
Reference in New Issue