【问题标题】:Integrating Amazon Polly with VBA将 Amazon Polly 与 VBA 集成
【发布时间】:2018-11-20 01:10:46
【问题描述】:

我想在 PowerPoint VBA 项目中使用 Amazon Polly 将笔记转换为 mp3。 我认为没有SDK,但是可以通过带有签名的API吗?

【问题讨论】:

  • 如果有人遇到问题:尝试 .net amazon C# SDK 并使用 VSTO 而不是 VBA。

标签: vba amazon-polly


【解决方案1】:

请参阅我最近构建的 VBA 模块VBA 类,允许从 MS Office 中直接调用 Amazon Web 服务 Polly,将生成的 MP3 保存到文件中.几点注意事项:

  • 我只确认在 Windows 10 上使用 Office 365 版本的 MS Word 和 MS Excel 成功调用了 Amazon Polly,但鉴于我过去的 VBA 经验,没有任何理由相信该代码不适用于其他变体.
  • 入口点是callAwsPolly
  • 您需要相应地调整 AWS 参数(即 aws.hostaws.regionaws.service 和 aws.uri)。
  • 凭证(aws.accessKey & aws.secretKey)是标准 AWS 示例凭证,显然需要用您自己的 AWS 凭证替换。李>
  • 请注意,在代码中嵌入凭据是非常糟糕的做法。不确定您的特定应用程序,但在我的情况下,我创建了自己的密钥库,可选择使用密码或 MAC 地址作为密钥库的解密密钥。凭证管理的概念是另一个主题,我不在这里讨论...

要将功能合并到 MS Office 产品中,请将以下内容添加为 VBA 模块...



Option Explicit

Sub callAwsPolly()

    Dim aws As amazonWebService
    Set aws = New amazonWebService

    aws.host = "polly.us-east-2.amazonaws.com"
    aws.region = Split(aws.host, ".")(1)
    aws.service = "polly"
    aws.uri = "/v1/speech"

    ' !!! NOTE:  The following is very bad practice to embed credentials in code!!!

    aws.accessKey = "AKIAIOSFODNN7EXAMPLE"
    aws.secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

    Dim requestParameters As String
    requestParameters = "{#OutputFormat#: #mp3#, #Text#: #Polly want a cracker?#, #TextType#: #ssml#, #VoiceId#: #Emma#}"
    requestParameters = Replace(requestParameters, "#", """")

    Dim httpResponse As Object
    Set httpResponse = aws.callWebService("application/json", requestParameters)

    If httpResponse Is Nothing Then
        MsgBox "Call to AWS Polly failed."
    ElseIf httpResponse.Status = 200 Then
        MsgBox "Call to AWS Polly succeeded!  MP3 file being saved."

        Dim iFile As Long:  iFile = FreeFile
        Open ActiveWorkbook.FullName & ".mp3" For Binary Access Write As #iFile
        Put iFile, , httpResponse.Responsebody
        Close #iFile
    Else
        MsgBox "Call to AWS Polly failed:" + CStr(httpResponse.Status) + " " + httpResponse.StatusText + " " + httpResponse.ResponseText
    End If

End Sub


' Adapted from https://stackoverflow.com/questions/36384741/cant-use-getbytes-and-computehash-methods-on-vba

Public Function hmacSha256(key As Variant, stringToHash As Variant) As Byte()

    Dim ssc As Object
    Set ssc = CreateObject("System.Security.Cryptography.HMACSHA256")

    ssc.key = str2byte(key)
    hmacSha256 = ssc.ComputeHash_2(str2byte(stringToHash))

    Set ssc = Nothing

End Function

Public Function sha256(stringToHash As Variant) As Byte()

    Dim ssc As Object
    Set ssc = CreateObject("System.Security.Cryptography.SHA256Managed")

    sha256 = ssc.ComputeHash_2(str2byte(stringToHash))

    Set ssc = Nothing

End Function

Public Function str2byte(s As Variant) As Byte()

    If VarType(s) = vbArray + vbByte Then
        str2byte = s
    ElseIf VarType(s) = vbString Then
        str2byte = StrConv(s, vbFromUnicode)
    Else
        Exit Function
    End If

End Function

Public Function byte2hex(byteArray() As Byte) As String

    Dim i As Long
    For i = 0 To UBound(byteArray)
        byte2hex = byte2hex & Right(Hex(256 Or byteArray(i)), 2)
    Next
    byte2hex = LCase(byte2hex)

End Function


...并将以下代码添加为 VBA 类模块,将其命名为“amazonWebService”...

'
' Class Module:  amazonWebService
'
Private className As String

Public host As String
Public region As String
Public service As String
Public uri As String

Public accessKey As String
Public secretKey As String

Private Sub Class_Initialize()

    className = "amazonWebService"

    host = ""
    region = ""
    uri = ""
    service = ""

    accessKey = ""
    secretKey = ""

End Sub

Public Function callWebService(contentType As String, requestParameters As String) As Object

    If host = "" Or region = "" Or uri = "" Or service = "" Or accessKey = "" Or secretKey = "" Then
        Err.Raise _
            1000, _
            className, _
            className + ": Please set properties before calling the amazon web service:  host, region, uri, service, accessKey, and secretKey."
        callWebService = Null
        Exit Function
    End If

    Dim amzDate As String, dateStamp As String
    amzDate = date2iso8601(GMT())
    dateStamp = Left(amzDate, 8)

    ' ************* TASK 1: CREATE A CANONICAL REQUEST *************
    ' http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

    'Step 1 is to define the verb (GET, POST, etc.)
    Dim method As String
    method = "POST"

    ' Step 2: Create canonical URI (set by the calling program)
    Dim canonicalUri As String
    canonicalUri = uri

    ' Step 3: Create the canonical query string. In this example (POST), request
    ' parameters are passed in the body of the request and the query string
    ' is blank.
    Dim canonicalQueryString As String
    canonicalQueryString = ""

    ' Step 4: Create the canonical headers. Header names must be trimmed
    ' and lowercase, and sorted in code point order from low to high.
    ' Note that there is a trailing \n.
    Dim canonicalHeaders As String
    canonicalHeaders = _
        "host:" + host + vbLf + _
        "x-amz-date:" + amzDate + vbLf

    ' Step 5: Create the list of signed headers. This lists the headers
    ' in the canonical_headers list, delimited with ";" and in alpha order.
    ' Note: The request can include any headers; canonical_headers and
    ' signed_headers include those that you want to be included in the
    ' hash of the request. "Host" and "x-amz-date" are always required.
    Dim signedHeaders As String
    signedHeaders = "host;x-amz-date"

    ' Step 6: Create payload hash. In this example, the payload (body of
    ' the request) contains the request parameters.
    Dim payloadHash As String
    payloadHash = byte2hex(sha256(requestParameters))

    ' Step 7: Combine elements to create canonical request
    Dim canonicalRequest As String
    canonicalRequest = method + vbLf + _
        canonicalUri + vbLf + _
        canonicalQueryString + vbLf + _
        canonicalHeaders + vbLf + _
        signedHeaders + vbLf + _
        payloadHash

    ' ************* TASK 2: CREATE THE STRING TO SIGN*************
    ' Match the algorithm to the hashing algorithm you use, either SHA-1 or
    ' SHA-256 (recommended)
    Dim algorithm As String, credentialScope As String, stringToSign As String
    algorithm = "AWS4-HMAC-SHA256"
    credentialScope = dateStamp + "/" + region + "/" + service + "/aws4_request"
    stringToSign = _
        algorithm + vbLf + _
        amzDate + vbLf + _
        credentialScope + vbLf + _
        byte2hex(sha256(canonicalRequest))

    ' ************* TASK 3: CALCULATE THE SIGNATURE *************
    ' Create the signing key using the function defined above.
    Dim signingKey() As Byte, signature As String
    signingKey = getSignatureKey(secretKey, dateStamp, region, service)

    ' Sign the string_to_sign using the signing_key
    signature = byte2hex(hmacSha256(signingKey, stringToSign))

    ' ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
    ' Put the signature information in a header named Authorization.
    Dim authorizationHeader As String
    authorizationHeader = _
        algorithm + " " + _
        "Credential=" + accessKey + "/" + credentialScope + ", " + _
        "SignedHeaders=" + signedHeaders + ", " + _
        "Signature=" + signature

    ' ************* SEND THE REQUEST TO AWS! *************

    Dim http As Object
    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")

    http.Open "POST", "https://" + host + uri, False
    http.setrequestheader "content-type", contentType
    http.setrequestheader "host", host
    http.setrequestheader "x-amz-date", amzDate
    http.setrequestheader "authorization", authorizationHeader

    http.Send requestParameters

    ' If the result is 403 Forbidden, then clear the current selected credentials.

    If http.Status = 403 Then
        accessKey = ""
        secretKey = ""
    End If

    ' Return the HTTP response back to the calling program.

    Set callWebService = http

End Function

Private Function GMT() As Date

    Dim dt As Object
    Set dt = CreateObject("WbemScripting.SWbemDateTime")

    dt.SetVarDate Now
    GMT = dt.GetVarDate(False)

    Set dt = Nothing

End Function

Private Function date2iso8601(dateTime As Date) As String

    date2iso8601 = Format(dateTime, "yyyymmdd\Thhnnss\Z")

End Function

Private Function getSignatureKey(key As String, dateStamp As String, regionName As String, serviceName As String) As Byte()
    Dim kDate() As Byte, kRegion() As Byte, kService() As Byte, kSigning() As Byte

    kDate = hmacSha256("AWS4" + key, dateStamp)
    kRegion = hmacSha256(kDate, regionName)
    kService = hmacSha256(kRegion, serviceName)
    kSigning = hmacSha256(kService, "aws4_request")

    getSignatureKey = kSigning

End Function

【讨论】:

  • 请把答案格式化一下,这样会更吸引人
  • @Billa,在查看了其他帖子作为参考后,采纳了您的建议并整理了我的答案。
  • @Trentium 感谢发帖...我非常渴望在 MS Access 中使用它(与亚马逊的 SP-API 一起使用),但我对所有的 id/ 有点困惑秘密以及我如何将这些映射到您的代码中。我有一个 Amazon 客户端 ID、一个秘密、一个刷新令牌(一个不会过期的密钥)......然后我使用这些来获取一个可以使用 60 分钟的令牌。但是您的代码似乎表明它只是需要的永久访问 ID 和秘密 - 所以我将我的代码放入您的代码中,但它错误地显示“请求中包含的安全令牌无效”...您能对此有所了解吗请问?
  • 所需的密钥类型是程序访问密钥...见docs.aws.amazon.com/general/latest/gr/…
  • 也许我措辞错误,我确定我知道什么是编程密钥……只是我在使用 AWS 4 签名(对于 SP-API)进行签名时遇到了麻烦。为了使用你的代码,今晚我注册了一个个人 AWS 账户……他们为我提供了一个 accessID 和密码……你的代码完美地与那些代码配合使用(做得好……那里的代码超级多)。 SPI-API 使用 LWA 凭据,我正在努力解决所有涉及的密钥/令牌(以及每个部署时)......但我也许可以使用你的代码来帮助我解决问题 -再次感谢。
猜你喜欢
  • 2017-11-17
  • 1970-01-01
  • 1970-01-01
  • 2016-09-22
  • 1970-01-01
  • 2019-05-23
  • 1970-01-01
  • 2022-10-02
  • 2021-04-25
相关资源
最近更新 更多