针对经过身份验证的用户构建转录应用程序 - Amazon SDK for JavaScript
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

Amazon SDK for JavaScript V3 API 参考指南详细描述了 Amazon SDK for JavaScript 版本 3 (V3) 的所有 API 操作。

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

针对经过身份验证的用户构建转录应用程序

在本教程中,您将学习如何:

  • 使用 Amazon Cognito 身份池实施身份验证,以接受与 Amazon Cognito 用户池联合的用户。

  • 使用 Amazon Transcribe 在浏览器中转录和显示录音。

情景

该应用程序使用户能够使用唯一的电子邮件和用户名进行注册。确认电子邮件后,他们可以录制语音消息,这些消息会自动转录并显示在应用程序中。

工作方式

该应用程序使用两个 Amazon S3 存储桶,一个用于托管应用程序代码,另一个用于存储转录。该应用程序使用 Amazon Cognito 用户池对您的用户进行身份验证。已经过身份验证的用户有 IAM 权限来访问所需的 Amazon 服务。

用户首次录制语音消息时,Amazon S3 会在 Amazon S3 存储桶中创建一个带有用户名的唯一文件夹,用于存储转录内容。Amazon Transcribe 将语音消息转录为文本,并将其以 JSON 格式保存在用户的文件夹中。当用户刷新应用程序时,其转录内容将会显示,可供下载或删除。

完成本教程大约需要 30 分钟。

步骤

先决条件

  • 设置项目环境以运行此节点 JavaScript 示例,并安装所需的 Amazon SDK for JavaScript 和第三方模块。请按照 GitHub 上的说明进行操作。

  • 使用用户凭证创建共享配置文件。有关提供共享凭证文件的更多信息,请参阅《Amazon SDK 和工具参考指南》中的共享配置和凭证文件

重要

此示例使用 ECMAScript6 (ES6)。这需要使用 Node.js 版本 13.x 或更高版本。要下载并安装最新版本的 Node.js,请参阅 Node.js 下载

但是,如果您更喜欢使用 CommonJS 语法,请参阅 JavaScript ES6/CommonJS 语法

创建 Amazon 资源

本节介绍如何使用 Amazon Cloud Development Kit (Amazon CDK) 为此应用程序预置 Amazon 资源。

注意

Amazon CDK 是一个软件开发框架,使您能够定义云应用程序资源。有关更多信息,请参阅 Amazon Cloud Development Kit (Amazon CDK) 开发人员指南

要为应用程序创建资源,请使用此处 GitHub 上的模板,使用 Amazon Web Services 管理控制台Amazon CLI 创建一个 Amazon CDK 堆栈。有关如何在完成本教程后修改堆栈或删除堆栈及其相关资源的说明,请参阅 GitHub 上的此处

注意

在一个 Amazon 区域和一个 Amazon 账户中,堆栈名称必须唯一。您最多可指定 128 个字符,支持数字和连字符。

生成的堆栈将自动预置以下资源。

  • 具有经过身份验证的用户角色的 Amazon Cognito 身份池。

  • 具有 Amazon S3 和 Amazon Transcribe 权限的 IAM 策略已附加到经过身份验证的用户角色。

  • Amazon Cognito 用户池,使用户能够注册和登录应用程序。

  • 用于托管应用程序文件的 Amazon S3 存储桶。

  • 用于存储转录的 Amazon S3 存储桶。

    重要

    此 Amazon S3 存储桶允许读取(列出)公开访问权限,这使任何人都可以列出存储桶中的对象并有可能滥用信息。如果您在完成本教程后没有立即删除此 Amazon S3 存储桶,我们强烈建议您遵守 Amazon Simple Storage Service 用户指南 中的 Amazon S3 安全最佳实践

创建 HTML

创建一个 index.html 文件,然后将以下内容复制并粘贴到文件中。该页面包含用于录制语音消息的按钮面板,以及一个显示当前用户的先前转录消息的表格。body 元素末尾的脚本标签调用 main.js,其中包含该应用程序的所有浏览器脚本。您可以使用 Webpack 创建 main.js,如本教程的下一部分所述。

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>title</title> <link rel="stylesheet" type="text/css" href="recorder.css"> <style> table, td { border: 1px solid black; } </style> </head> <body> <h2>Record</h2> <p> <button id="record" onclick="startRecord()"></button> <button id="stopRecord" disabled onclick="stopRecord()">Stop</button> <p id="demo" style="visibility: hidden;"></p> </p> <p> <audio id="recordedAudio"></audio> </p> <h2>My transcriptions</h2> <table id="myTable1" style ="width:678px;"> </table> <table id="myTable" style ="width:678px;"> <tr> <td style = "font-weight:bold">Time created</td> <td style = "font-weight:bold">Transcription</td> <td style = "font-weight:bold">Download</td> <td style = "font-weight:bold">Delete</td> </tr> </table> <script type="text/javascript" src="./main.js"></script> </body> </html>

GitHub 的此处提供了此代码示例。

准备浏览器脚本

您需要使用 Webpack 将三个文件 index.htmlrecorder.jshelper.js 捆绑成一个 main.js。本节仅详细描述了 index.js 中使用 SDK for JavaScript(可在GitHub 上的此处找到)的函数。

注意

您也需要 recorder.jshelper.js,但是由于它们不包含 Node.js 代码,因此分别在 GitHub 上此处此处的内嵌注释中进行了说明。

首先,定义参数。COGNITO_ID 是您在本教程的创建 Amazon 资源 主题中创建的 Amazon Cognito 用户池的端点。其格式为 cognito-idp.AWS_REGION.amazonaws.com/USER_POOL_ID。用户池 ID 是 Amazon 凭证令牌中的 ID_TOKEN,由“helper.js”文件中的 getToken 函数从应用程序 URL 中删除。此令牌将传递至 loginData 变量,该变量为 Amazon Transcribe 和 Amazon S3 客户端对象提供登录信息。将 "REGION" 替换为 Amazon 区域,将 "BUCKET" 替换为您的存储桶,将 "IDENTITY_POOL_ID" 替换为您为此示例创建的 Amazon Cognito 身份池的示例页面中的 IdentityPoolId。这也被传递给每个客户端对象。

// Import the required AWS SDK clients and commands for Node.js import "./helper.js"; import "./recorder.js"; import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity"; import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity"; import { CognitoIdentityProviderClient, GetUserCommand, } from "@aws-sdk/client-cognito-identity-provider"; import { S3RequestPresigner } from "@aws-sdk/s3-request-presigner"; import { createRequest } from "@aws-sdk/util-create-request"; import { formatUrl } from "@aws-sdk/util-format-url"; import { TranscribeClient, StartTranscriptionJobCommand, } from "@aws-sdk/client-transcribe"; import { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsCommand, DeleteObjectCommand, } from "@aws-sdk/client-s3"; import fetch from "node-fetch"; // Set the parameters. // 'COGINTO_ID' has the format 'cognito-idp.eu-west-1.amazonaws.com/COGNITO_ID'. let COGNITO_ID = "COGNITO_ID"; // Get the Amazon Cognito ID token for the user. 'getToken()' is in 'helper.js'. let idToken = getToken(); let loginData = { [COGNITO_ID]: idToken, }; const params = { Bucket: "BUCKET", // The Amazon Simple Storage Solution (S3) bucket to store the transcriptions. Region: "REGION", // The AWS Region identityPoolID: "IDENTITY_POOL_ID", // Amazon Cognito Identity Pool ID. }; // Create an Amazon Transcribe service client object. const client = new TranscribeClient({ region: params.Region, credentials: fromCognitoIdentityPool({ client: new CognitoIdentityClient({ region: params.Region }), identityPoolId: params.identityPoolID, logins: loginData, }), }); // Create an Amazon S3 client object. const s3Client = new S3Client({ region: params.Region, credentials: fromCognitoIdentityPool({ client: new CognitoIdentityClient({ region: params.Region }), identityPoolId: params.identityPoolID, logins: loginData, }), });

HTML 页面加载后,如果这是用户首次登录该应用程序,则 updateUserInterface 会在 Amazon S3 存储桶中创建一个带有用户名的文件夹。如果不是首次登录,它会使用用户先前会话中的任何转录更新用户界面。

window.onload = async () => { // Set the parameters. const userParams = { // Get the access token. 'GetAccessToken()' is in 'helper.js'. AccessToken: getAccessToken(), }; // Create a CognitoIdentityProviderClient client object. const client = new CognitoIdentityProviderClient({ region: params.Region }); try { const data = await client.send(new GetUserCommand(userParams)); const username = data.Username; // Export username for use in 'recorder.js'. exports.username = username; try { // If this is user's first sign-in, create a folder with user's name in Amazon S3 bucket. // Otherwise, no effect. const Key = `${username}/`; try { const data = await s3Client.send( new PutObjectCommand({ Key: Key, Bucket: params.Bucket }) ); console.log("Folder created for user ", data.Username); } catch (err) { console.log("Error", err); } try { // Get a list of the objects in the Amazon S3 bucket. const data = await s3Client.send( new ListObjectsCommand({ Bucket: params.Bucket, Prefix: username }) ); // Create a variable for the list of objects in the Amazon S3 bucket. const output = data.Contents; // Loop through the objects, populating a row on the user interface for each object. for (var i = 0; i < output.length; i++) { var obj = output[i]; const objectParams = { Bucket: params.Bucket, Key: obj.Key, }; // Get the name of the object from the Amazon S3 bucket. const data = await s3Client.send(new GetObjectCommand(objectParams)); // Extract the body contents, a readable stream, from the returned data. const result = data.Body; // Create a variable for the string version of the readable stream. let stringResult = ""; // Use 'yieldUnit8Chunks' to convert the readable streams into JSON. for await (let chunk of yieldUint8Chunks(result)) { stringResult += String.fromCharCode.apply(null, chunk); } // The setTimeout function waits while readable stream is converted into JSON. setTimeout(function () { // Parse JSON into human readable transcript, which will be displayed on user interface (UI). const outputJSON = JSON.parse(stringResult).results.transcripts[0].transcript; // Create name for transcript, which will be displayed. const outputJSONTime = JSON.parse(stringResult) .jobName.split("/")[0] .replace("-job", ""); i++; // // Display the details for the transcription on the UI. // 'displayTranscriptionDetails()' is in 'helper.js'. displayTranscriptionDetails( i, outputJSONTime, objectParams.Key, outputJSON ); }, 1000); } } catch (err) { console.log("Error", err); } } catch (err) { console.log("Error creating presigned URL", err); } } catch (err) { console.log("Error", err); } }; // Convert readable streams. async function* yieldUint8Chunks(data) { const reader = data.getReader(); try { while (true) { const { done, value } = await reader.read(); if (done) return; yield value; } } finally { reader.releaseLock(); } }

当用户录制语音消息进行转录时,upload 会将录音上传到 Amazon S3 存储桶。此函数是从 recorder.js 文件中调用的。

// Upload recordings to Amazon S3 bucket window.upload = async function (blob, userName) { // Set the parameters for the recording recording. const Key = `${userName}/test-object-${Math.ceil(Math.random() * 10 ** 10)}`; let signedUrl; // Create a presigned URL to upload the transcription to the Amazon S3 bucket when it is ready. try { // Create an Amazon S3RequestPresigner object. const signer = new S3RequestPresigner({ ...s3Client.config }); // Create the request. const request = await createRequest( s3Client, new PutObjectCommand({ Key, Bucket: params.Bucket }) ); // Define the duration until expiration of the presigned URL. const expiration = new Date(Date.now() + 60 * 60 * 1000); // Create and format the presigned URL. signedUrl = formatUrl(await signer.presign(request, expiration)); console.log(`\nPutting "${Key}"`); } catch (err) { console.log("Error creating presigned URL", err); } try { // Upload the object to the Amazon S3 bucket using a presigned URL. response = await fetch(signedUrl, { method: "PUT", headers: { "content-type": "application/octet-stream", }, body: blob, }); // Create the transcription job name. In this case, it's the current date and time. const today = new Date(); const date = today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate(); const time = today.getHours() + "-" + today.getMinutes() + "-" + today.getSeconds(); const jobName = date + "-time-" + time; // Call the "createTranscriptionJob()" function. createTranscriptionJob( "s3://" + params.Bucket + "/" + Key, jobName, params.Bucket, Key ); } catch (err) { console.log("Error uploading object", err); } }; // Create the AWS Transcribe transcription job. const createTranscriptionJob = async (recording, jobName, bucket, key) => { // Set the parameters for transcriptions job const params = { TranscriptionJobName: jobName + "-job", LanguageCode: "en-US", // For example, 'en-US', OutputBucketName: bucket, OutputKey: key, Media: { MediaFileUri: recording, // For example, "https://transcribe-demo.s3-REGION.amazonaws.com/hello_world.wav" }, }; try { // Start the transcription job. const data = await client.send(new StartTranscriptionJobCommand(params)); console.log("Success - transcription submitted", data); } catch (err) { console.log("Error", err); } };

deleteTranscription 从用户界面中删除转录,deleteRow 从 Amazon S3 存储桶中删除现有转录。两者均由用户界面上的删除按钮触发。

// Delete a transcription from the Amazon S3 bucket. window.deleteJSON = async (jsonFileName) => { try { await s3Client.send( new DeleteObjectCommand({ Bucket: params.Bucket, Key: jsonFileName, }) ); console.log("Success - JSON deleted"); } catch (err) { console.log("Error", err); } }; // Delete a row from the user interface. window.deleteRow = function (rowid) { const row = document.getElementById(rowid); row.parentNode.removeChild(row); };

最后,在命令提示符中运行以下命令,将本示例的 JavaScript 捆绑到名为 main.js 的文件中:

webpack index.js --mode development --target web --devtool false -o main.js
注意

有关安装 Webpack 的信息,请参阅将应用程序与 webpack 捆绑在一起

运行应用程序

您可以在下面的位置查看该应用程序。

DOMAIN/login?client_id=APP_CLIENT_ID&response_type=token&scope=aws.cognito.signin.user.admin+email+openid+phone+profile&redirect_uri=REDIRECT_URL

Amazon Cognito 在 Amazon Web Services 管理控制台中提供链接,支持您轻松运行该应用程序。只需导航到 Amazon Cognito 用户池的应用程序客户端设置,然后选择启动托管 UI 即可。该应用程序的 URL 采用以下格式。

重要

托管 UI 的响应类型默认为“code”。但是,本教程专为“token”响应类型而设计,因此您必须对其进行更改。

删除 Amazon 资源

完成本教程后,您应删除相关资源,以免产生任何不必要的费用。由于您已将内容添加到两个 Amazon S3 存储桶,因此必须手动将其删除。然后,您可以使用 Amazon Web Services 管理控制台Amazon CLI 删除剩余的资源。有关如何修改堆栈或在完成本教程后删除堆栈及其相关资源的说明,请参阅 GitHub 上的此处