最近學(xué)習(xí)微信小程序,其實對于ocr研究過程中的具體實現(xiàn)做個一個整理
- 需求
- 前期準(zhǔn)備
- 思路導(dǎo)圖
- 代碼實現(xiàn)
1.需求
微信小程序拍照或者識別圖片中的文字內(nèi)容
2.前期準(zhǔn)備
2.1 客戶端微信小程序: appid, secret 用來獲取access_token
image.png
2.2 基于nodejs開發(fā)的服務(wù)端(express, mongondb)
2.3 小程序添加OCR插件
image.png
2.4 注意點: 一定要配置uploadFile域名,不然wx.uploadFile上傳圖片無法返回數(shù)據(jù)
image.png
3. 思維導(dǎo)圖
image.png
4. 實現(xiàn)
4.1 小程序
server.js
對整個app網(wǎng)絡(luò)請求封裝的工具
const baseUrl = 'https://XXXXXX.cn'
const getHttp = (url, data, success, fail, complete) => {
wx.request({
url: baseUrl+url,
data: data,
header: {
"Content-Type": "application/json"
},
success: success,
fail: fail,
complete: complete
})
}
const getToken = () => {
return new Promise((resolve, reject) => {
getHttp("/getToken", null, (res) => {
resolve(res)
}, null, null);
})
}
module.exports = {
...
getToken: getToken
}
- app.js 程序啟動獲取接口調(diào)用憑據(jù)
App({
onLaunch: function (val) {
const getAccessToken = function () {
let tokenRes = server.getToken()
let tokenResData = tokenRes.data
if (tokenResData.status === 0) {
let token = tokenResData.data
wx.setStorageSync('token', token)
} else {
wx.showToast({
title: '獲取api token失敗',
})
}
}
getAccessToken()
}
})
- 解析圖片頁面實現(xiàn)
chooseImg: async function () {
var self = this
const getAccessToken = async function () {
return new Promise((resolve, reject) => {
let token = wx.getStorageSync('token')
resolve(token)
})
}
const chooseImage = async function () {
return new Promise((resolve, reject) => {
wx.chooseMedia({
count: 1,
mediaType: 'image',
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success(res) {
// tempFilePaths可以作為img標(biāo)簽的src屬性顯示圖片,回調(diào)結(jié)果
const tempFilePaths = res.tempFiles
resolve(tempFilePaths[0])
}
})
})
}
// 1. 選擇圖片
let temp = await chooseImage()
if (!temp) return
self.setData({
searching: true
})
// 2. 獲取api access_token
let accessToken = await getAccessToken()
if (!accessToken) {
self.setData({
searching: false
})
return
}
let url = server.baseUrl+'/parseImage?access_token='+accessToken
// 3. 上傳圖片
wx.uploadFile({
url: url,
filePath: temp.tempFilePath,
name: 'img',
formData: {
contentType: 'image/png',
value:"",
},
success (res){
let parseData = JSON.parse(res.data)
if (parseData.status === 0 && parseData.data && parseData.data.items && parseData.data.items.length > 0) {
let {items} = parseData.data
// item即解析出的圖片文字?jǐn)?shù)組,包含文字的位置信息以及內(nèi)容
} else {
wx.showToast({
title: '圖片解析失敗',
})
}
},
fail (err) {
self.setData({
items: newItems,
searching: false
})
wx.showToast({
title: '圖片上傳失敗',
})
}
})
}
4.2 服務(wù)端
由于小程序代碼中不能暴露appid和secret,不然審核不通過,因此把這倆參數(shù)記錄在服務(wù)端
先獲取OCR需要的參數(shù)之一: access_token
- 第一步:獲取access_token
router.get('/getToken', function (req, res) {
let url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appId+"&secret="+appSerect
let p = new Promise((resolve, reject) => {
https.get(url, (response) => {
let todo = '';
// called when a data chunk is received.
response.on('data', (chunk) => {
todo += chunk;
});
// called when the complete response is received.
response.on('end', () => {
let resData = JSON.parse(todo)
resolve(resData)
});
}).on("error", (error) => {
reject(error)
// console.log("Error: " + error.message);
});
})
p.then(_ => {
// console.log(_.access_token)
res.send_data({
status: 0,
data: Base64.encode(_.access_token) // 加密返回
});
})
});
- 第二步: 解析圖片內(nèi)容
1.把上傳的圖片保存到服務(wù)器,返回一個獲取圖片地址的url
2.根據(jù)上一步獲取的url+access_token,調(diào)用api解析圖片
router.post('/parseImage', function (req, res) {
if (!req.files)
return res.status(400).send('No image were uploaded.');
let img = req.files.img;
// 1. 圖片寫入本地(簡單化處理,返回的其實就是圖片的名稱)
let writeP = OrderItem.uploadImage(img)
// 2. 拼接圖片地址傳給小程序解析圖片的api
writeP.then(_ => {
// 2.1 解析圖片地址
let imageLocalPath = _.data ? _.data : ''
// 獲取圖片地址的url,作為參數(shù)傳給微信小程序api,會根據(jù)url加載圖片
let imgPath = `https://XXXXXXX.cn/api/write/image/${imageLocalPath}`
// 2.2 解析token(之前返回客戶端的是加密后的,因此這里解密之后傳入)
let token = Base64.decode(req.query.access_token)
let wxurl = 'https://api.weixin.qq.com/cv/ocr/comm?access_token='+token+'&img_url='+imgPath
axios({
url: wxurl,
method: 'post'
})
.then(parseData => {
// 3. 刪除本地圖片
OrderItem.deleteImage(_.data, (err) => {
console.log(err)
})
res.send_data({
status: 0,
data: parseData.data
});
return _
})
.catch(error => {
// console.error(error);
res.send_data({
status: 400,
data: error
});
return _
});
})
讀取圖片實現(xiàn)
// route.js文件
router.get('/write/image/:path',function(req, res) {
let path = req.params.path
if (!path)
return res.status(400).send('No image path were read.');
res.writeHead(200,{'Content-Type':'image/jpeg'});
OrderItem.readImage(req.params.path).then(file => {
res.write(file,'binary');
res.end();
})
})
OrderItem.js文件
const fs = require('fs')
function uploadImage(img) {
// 寫入本地
let path = `${__dirname}/../../write/${img.name}`;
return new Promise((resolve, reject) => {
fs.writeFile(path, img.data, 'binary', (err) => {
if (err) {
console.log('寫入文件錯誤')
reject({
data: false
})
} else {
console.log('寫入文件成功')
resolve({
data: Base64.encode(img.name)
})
}
})
})
}
function readImage(path) {
let imageName = Base64.decode(path)
return new Promise((resolve, reject) => {
fs.readFile(`${__dirname}/../../write/${imageName}`,'binary',function(err,file){
if(err){
console.log(err);
reject(err)
return;
}else{
console.log('輸出圖片');
resolve(file)
}
})
})
}
module.exports = {
...
uploadImage,
readImage,
deleteImage
}



