利用 Azure 认知服务
Azure 认知服务是一组基于云的 API,可在 AI 应用程序和数据流中使用。 它提供可在应用程序中使用的预先训练的模型,无需数据,也无需进行模型训练。 它们可以通过 HTTP REST 接口轻松集成到应用程序中。
在“使用 Webhook”教程中,你已了解如何将 Webhook 与应用程序配合使用,以在更新现有文件或上传新文件时获取通知。 本教程介绍如何将其与 Azure 认知服务连接以从发票中提取数据。
若要在容器发生更改时使用当前 SharePoint 应用程序设置自动 AI 处理,需要遵循 使用 Webhook, 然后:
- 获取容器的增量更改。 你当前可以在容器中发生任何更改时收到通知,并且现在将获取添加或更新的文件。
- 调用 Azure 认知服务的文档智能服务 API。 需要创建一个 Azure AI 资源,以使用该 API 从图像中提取字段并获取提取的文件。 可以按照本教程中所示存储它们,也可以根据需要处理它们。
提示
若要详细了解本教程中使用的 Microsoft Graph API,请参阅 跟踪驱动器的更改、 获取 DriveItem 资源以及 上传或替换 DriveItem 的内容。
获取容器的增量更改
打开 GraphProvider.ts 并实现 方法 getDriveChanges
以获取已更改项的列表:
public static async getDriveChanges(driveId: string): Promise<any[]> {
let changedItems: any[] = [];
const driveDeltaBasePath: string = `/drives/${driveId}/items/root/delta`;
let driveDeltaTokenParams: string = "";
let hasMoreChanges: boolean = true;
try{
do {
if (this.changeTokens.has(driveId)) {
driveDeltaTokenParams = `?token=${this.changeTokens.get(driveId)}`
}
const response = await this.graphClient.api(driveDeltaBasePath + driveDeltaTokenParams).get();
changedItems.push(...response.value);
if (response['@odata.nextLink']) {
const token = new URL(response['@odata.nextLink']).searchParams.get('token');
this.changeTokens.set(driveId, token);
} else {
hasMoreChanges = false;
const token = new URL(response['@odata.deltaLink']).searchParams.get('token');
this.changeTokens.set(driveId, token);
}
console.log(this.changeTokens.get(driveId));
} while (hasMoreChanges);
}
catch(err){
console.log(err);
}
return changedItems;
}
实现 方法 getDriveItem
以从容器中提取文件:
public static async getDriveItem(driveId: string, itemId: string): Promise<any> {
return await this.graphClient.api(`/drives/${driveId}/items/${itemId}`).get();
}
ReceiptProcessor.ts创建一个新文件,并实现 方法 processDrive
:
export abstract class ReceiptProcessor {
public static async processDrive(driveId: string): Promise<void> {
const changedItems = await GraphProvider.getDriveChanges(driveId);
for (const changedItem of changedItems) {
try {
const item = await GraphProvider.getDriveItem(driveId, changedItem.id);
const extension = this.getFileExtension(item.name);
if (this.SUPPORTED_FILE_EXTENSIONS.includes(extension.toLowerCase())) {
console.log(item.name);
const url = item["@microsoft.graph.downloadUrl"];
const receipt = await this.analyzeReceiptStream(await this.getDriveItemStream(url));
const receiptString = JSON.stringify(receipt, null, 2)
const fileName = this.getFileDisplayName(item.name) + "-extracted-fields.json";
const parentId = item.parentReference.id;
await GraphProvider.addDriveItem(driveId, parentId, fileName, receiptString);
}
} catch (error) {
console.log(error);
}
}
}
}
此时,如果重启应用以及隧道和订阅,则应会看到控制台中列出的最近添加/更新的文件。
调用 Azure 认知服务的文档智能服务 API
若要使用 Azure 认知服务文档智能 API,需要为 Azure AI 服务创建多服务或文档智能资源。 请参阅以下教程来创建资源:
完成此步骤后,应有一个终结点和一个可供使用的密钥。
现在打开 ReceiptProcessor.ts ,创建用于存储 Azure 认知服务凭据的方法 dac
:
private static dac = new DocumentAnalysisClient(
`${process.env["DAC_RESOURCE_ENDPOINT"]}`,
new AzureKeyCredential(`${process.env["DAC_RESOURCE_KEY"]}`)
);
Create 方法 getDriveItemStream
。
private static async getDriveItemStream(url: string): Promise<Readable> {
const token = GraphProvider.graphAccessToken;
const config: AxiosRequestConfig = {
method: "get",
url: url,
headers: {
"Authorization": `Bearer ${token}`
},
responseType: 'stream'
};
const response = await axios.get<Readable>(url, config);
return response.data;
}
创建方法 analyzeReceiptStream
以通过 Azure 认知服务处理获取 OCR 字段。 此处我们采用模型 prebuilt-invoice
,但可以选择其他模型:
private static async analyzeReceiptStream(stream: Readable): Promise<any> {
const poller = await this.dac.beginAnalyzeDocument("prebuilt-invoice", stream, {
onProgress: ({ status }) => {
console.log(`status: ${status}`);
},
});
const {
documents: [result] = [],
} = await poller.pollUntilDone();
const fields = result?.fields;
this.removeUnwantedFields(fields);
return fields;
}
创建方法 removeUnwantedFields
以删除 Azure 认知服务响应中不需要的字段:
private static removeUnwantedFields(fields: any) {
for (const prop in fields) {
if (prop === 'boundingRegions' || prop === 'content' || prop === 'spans') {
delete fields[prop];
}
if (typeof fields[prop] === 'object') {
this.removeUnwantedFields(fields[prop]);
}
}
}
最后,打开GraphProvider.ts,在 类的GraphProvider
末尾添加 addDriveItem
方法。
public static async addDriveItem(driveId: string, parentId: any, fileName: string, receiptString: string) {
await this.graphClient.api(`/drives/${driveId}/items/${parentId}:/${fileName}:/content`).put(receiptString);
}
现在,重启演示应用,并在容器上使用 ngrok 和增量更改订阅再次设置隧道。
如果在此容器中添加/更新 (支持格式的任何文件:JPEG、JPG、PNG、BMP、TIFF、PDF) ,应会看到创建并包含从文件中提取的字段的新 JSON 文件。