168 lines
4.7 KiB
JavaScript
168 lines
4.7 KiB
JavaScript
;(function (global) {
|
||
"use strict";
|
||
|
||
function isFileLike(v) {
|
||
return (typeof File !== "undefined" && v instanceof File) ||
|
||
(typeof Blob !== "undefined" && v instanceof Blob);
|
||
}
|
||
|
||
function fileToDataURL(file) {
|
||
return new Promise(function (resolve, reject) {
|
||
var reader = new FileReader();
|
||
reader.onload = function () {
|
||
resolve(reader.result);
|
||
};
|
||
reader.onerror = reject;
|
||
reader.readAsDataURL(file);
|
||
});
|
||
}
|
||
|
||
async function normalizeImageInput(input) {
|
||
if (typeof input === "string") {
|
||
return {
|
||
image_base64: input,
|
||
filename: "upload_base64",
|
||
content_type: null
|
||
};
|
||
}
|
||
|
||
if (isFileLike(input)) {
|
||
var dataURL = await fileToDataURL(input);
|
||
return {
|
||
image_base64: dataURL,
|
||
filename: input.name || "upload_file",
|
||
content_type: input.type || null
|
||
};
|
||
}
|
||
|
||
throw new Error("只支持 base64 字符串、File 或 Blob");
|
||
}
|
||
|
||
async function parseJSONResponse(resp) {
|
||
var text = await resp.text();
|
||
var data = null;
|
||
|
||
try {
|
||
data = text ? JSON.parse(text) : null;
|
||
} catch (e) {
|
||
data = text;
|
||
}
|
||
|
||
if (!resp.ok) {
|
||
var msg = data && data.detail ? data.detail : text || ("HTTP " + resp.status);
|
||
throw new Error(msg);
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
function normalizeSign(rawSign, fallbackAppId) {
|
||
if (rawSign === undefined || rawSign === null || rawSign === "") {
|
||
throw new Error("sign 不能为空,必须由客户后端传入签名字符串或对象");
|
||
}
|
||
|
||
var data = rawSign;
|
||
if (typeof rawSign === "string") {
|
||
try {
|
||
data = JSON.parse(rawSign);
|
||
} catch (e) {
|
||
throw new Error('sign 字符串必须是 JSON 字符串,例如 {"timestamp":"1718123456","signature":"xxxx"}');
|
||
}
|
||
}
|
||
|
||
if (!data || typeof data !== "object") {
|
||
throw new Error("sign 必须是对象,或可解析为对象的 JSON 字符串");
|
||
}
|
||
|
||
var appId = data.app_id || data.appId || fallbackAppId;
|
||
var timestamp = data.timestamp;
|
||
var signature = data.signature;
|
||
|
||
if (!appId || !timestamp || !signature) {
|
||
throw new Error("sign 必须包含 timestamp 和 signature;app_id 可省略并回退到 config.appId");
|
||
}
|
||
|
||
return {
|
||
app_id: String(appId),
|
||
timestamp: String(timestamp),
|
||
signature: String(signature)
|
||
};
|
||
}
|
||
|
||
function PassportOCRClient(config) {
|
||
this.apiBase = (config.apiBase || "").replace(/\/+$/, "");
|
||
this.appId = config.appId || "";
|
||
this.sign = config.sign;
|
||
|
||
if (!this.apiBase) {
|
||
throw new Error("apiBase 不能为空");
|
||
}
|
||
if (!this.appId) {
|
||
throw new Error("appId 不能为空");
|
||
}
|
||
}
|
||
|
||
PassportOCRClient.prototype.setSign = function (sign) {
|
||
this.sign = sign;
|
||
return this;
|
||
};
|
||
|
||
PassportOCRClient.prototype.getSignature = function (signOverride) {
|
||
var rawSign = signOverride !== undefined ? signOverride : this.sign;
|
||
return normalizeSign(rawSign, this.appId);
|
||
};
|
||
|
||
PassportOCRClient.prototype.callOCR = async function (path, input, options) {
|
||
options = options || {};
|
||
|
||
var imageInfo = await normalizeImageInput(input);
|
||
var payload = {
|
||
filename: options.filename || imageInfo.filename,
|
||
content_type: options.content_type || imageInfo.content_type,
|
||
image_base64: imageInfo.image_base64,
|
||
text_rec_score_thresh: options.text_rec_score_thresh ?? 0.3,
|
||
mrz_text_rec_score_thresh: options.mrz_text_rec_score_thresh ?? 0.0,
|
||
mrz_top_ratio: options.mrz_top_ratio ?? 0.68,
|
||
return_debug: !!options.return_debug,
|
||
auto_rotate: options.auto_rotate ?? true
|
||
};
|
||
|
||
var signed = this.getSignature(options.sign);
|
||
|
||
var resp = await fetch(this.apiBase + path, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-App-Id": signed.app_id,
|
||
"X-Timestamp": signed.timestamp,
|
||
"X-Signature": signed.signature
|
||
},
|
||
body: JSON.stringify(payload)
|
||
});
|
||
|
||
return await parseJSONResponse(resp);
|
||
};
|
||
|
||
PassportOCRClient.prototype.recognizePassport = function (input, options) {
|
||
return this.callOCR("/ocr/passport/base64", input, options);
|
||
};
|
||
|
||
PassportOCRClient.prototype.recognizeMRZ = function (input, options) {
|
||
return this.callOCR("/ocr/mrz/base64", input, options);
|
||
};
|
||
|
||
PassportOCRClient.prototype.recognizeIdCard = function (input, options) {
|
||
return this.callOCR("/ocr/idcard/base64", input, options);
|
||
};
|
||
|
||
PassportOCRClient.prototype.recognizeVisaNumber = function (input, options) {
|
||
return this.callOCR("/ocr/visa-number/base64", input, options);
|
||
};
|
||
|
||
global.PassportOCRSDK = {
|
||
create: function (config) {
|
||
return new PassportOCRClient(config || {});
|
||
}
|
||
};
|
||
})(window);
|