arrow_back

使用 Go 和 Cloud Run 创建 PDF 文件

登录 加入
访问 700 多个实验和课程

使用 Go 和 Cloud Run 创建 PDF 文件

实验 1 小时 universal_currency_alt 5 个积分 show_chart 中级
info 此实验可能会提供 AI 工具来支持您学习。
访问 700 多个实验和课程

GSP762

Google Cloud 自学实验的徽标

概览

在本实验中,您将在 Cloud Run 无服务器服务上构建一个用于转换 PDF 的 Web 应用,该应用会自动将存储在 Google 云端硬盘中的文件转换为 PDF 文件并存储在单独的 Google 云端硬盘文件夹中。

目标

在本实验中,您将执行以下操作:

  • 将 Go 应用转换为容器
  • 了解如何使用 Google Cloud Build 构建容器
  • 创建一个在云端将文件转换为 PDF 文件的 Cloud Run 服务。
  • 了解如何创建服务账号并添加权限
  • 将事件处理与 Cloud Storage 结合使用

设置和要求

点击“开始实验”按钮前的注意事项

请阅读以下说明。实验是计时的,并且您无法暂停实验。计时器在您点击开始实验后即开始计时,显示 Google Cloud 资源可供您使用多长时间。

此实操实验可让您在真实的云环境中开展实验活动,免受模拟或演示环境的局限。为此,我们会向您提供新的临时凭据,您可以在该实验的规定时间内通过此凭据登录和访问 Google Cloud。

为完成此实验,您需要:

  • 能够使用标准的互联网浏览器(建议使用 Chrome 浏览器)。
注意:请使用无痕模式(推荐)或无痕浏览器窗口运行此实验。这可以避免您的个人账号与学生账号之间发生冲突,这种冲突可能导致您的个人账号产生额外费用。
  • 完成实验的时间 - 请注意,实验开始后无法暂停。
注意:请仅使用学生账号完成本实验。如果您使用其他 Google Cloud 账号,则可能会向该账号收取费用。

如何开始实验并登录 Google Cloud 控制台

  1. 点击开始实验按钮。如果该实验需要付费,系统会打开一个对话框供您选择支付方式。左侧是“实验详细信息”窗格,其中包含以下各项:

    • “打开 Google Cloud 控制台”按钮
    • 剩余时间
    • 进行该实验时必须使用的临时凭据
    • 帮助您逐步完成本实验所需的其他信息(如果需要)
  2. 点击打开 Google Cloud 控制台(如果您使用的是 Chrome 浏览器,请右键点击并选择在无痕式窗口中打开链接)。

    该实验会启动资源并打开另一个标签页,显示“登录”页面。

    提示:将这些标签页安排在不同的窗口中,并排显示。

    注意:如果您看见选择账号对话框,请点击使用其他账号
  3. 如有必要,请复制下方的用户名,然后将其粘贴到登录对话框中。

    {{{user_0.username | "<用户名>"}}}

    您也可以在“实验详细信息”窗格中找到“用户名”。

  4. 点击下一步

  5. 复制下面的密码,然后将其粘贴到欢迎对话框中。

    {{{user_0.password | "<密码>"}}}

    您也可以在“实验详细信息”窗格中找到“密码”。

  6. 点击下一步

    重要提示:您必须使用实验提供的凭据。请勿使用您的 Google Cloud 账号凭据。 注意:在本实验中使用您自己的 Google Cloud 账号可能会产生额外费用。
  7. 继续在后续页面中点击以完成相应操作:

    • 接受条款及条件。
    • 由于这是临时账号,请勿添加账号恢复选项或双重验证。
    • 请勿注册免费试用。

片刻之后,系统会在此标签页中打开 Google Cloud 控制台。

注意:如需访问 Google Cloud 产品和服务,请点击导航菜单,或在搜索字段中输入服务或产品的名称。 “导航菜单”图标和“搜索”字段

激活 Cloud Shell

Cloud Shell 是一种装有开发者工具的虚拟机。它提供了一个永久性的 5GB 主目录,并且在 Google Cloud 上运行。Cloud Shell 提供可用于访问您的 Google Cloud 资源的命令行工具。

  1. 点击 Google Cloud 控制台顶部的激活 Cloud Shell “激活 Cloud Shell”图标

  2. 在弹出的窗口中执行以下操作:

    • 继续完成 Cloud Shell 信息窗口中的设置。
    • 授权 Cloud Shell 使用您的凭据进行 Google Cloud API 调用。

如果您连接成功,即表示您已通过身份验证,且项目 ID 会被设为您的 Project_ID 。输出内容中有一行说明了此会话的 Project_ID

Your Cloud Platform project in this session is set to {{{project_0.project_id | "PROJECT_ID"}}}

gcloud 是 Google Cloud 的命令行工具。它已预先安装在 Cloud Shell 上,且支持 Tab 自动补全功能。

  1. (可选)您可以通过此命令列出活跃账号名称:
gcloud auth list
  1. 点击授权

输出:

ACTIVE: * ACCOUNT: {{{user_0.username | "ACCOUNT"}}} To set the active account, run: $ gcloud config set account `ACCOUNT`
  1. (可选)您可以通过此命令列出项目 ID:
gcloud config list project

输出:

[core] project = {{{project_0.project_id | "PROJECT_ID"}}} 注意:如需查看在 Google Cloud 中使用 gcloud 的完整文档,请参阅 gcloud CLI 概览指南

架构

在本实验中,您将帮助 Pet Theory 宠物医院自动将其账单转换为 PDF 文件,以确保客户可以顺利打开账单。

架构图

使用 Google API

在本实验中,您将使用 Google API。以下 API 已为您启用:

名称 API
Cloud Build cloudbuild.googleapis.com
Cloud Storage storage-component.googleapis.com
Cloud Run run.googleapis.com

任务 1. 获取源代码

从下载本实验必需的代码开始。

  1. 激活实验账号:

    gcloud auth list --filter=status:ACTIVE --format="value(account)"
  2. 运行以下命令以克隆 Pet Theory 库:

    git clone https://github.com/Deleplace/pet-theory.git
  3. 移动到正确的目录:

    cd pet-theory/lab03

任务 2. 创建账单微服务

在此部分中,您将创建一个 Go 应用来处理请求。如架构图所示,您将在解决方案中集成 Cloud Storage。

  1. 点击打开编辑器图标,然后点击在新窗口中打开

  2. 前往 pet-theory > lab03 > server.go

  3. 打开 server.go 源代码并进行编辑,使之与以下文本匹配:

    package main import ( "fmt" "io/ioutil" "log" "net/http" "os" "os/exec" "regexp" "strings" ) func main() { http.HandleFunc("/", process) port := os.Getenv("PORT") if port == "" { port = "8080" log.Printf("Defaulting to port %s", port) } log.Printf("Listening on port %s", port) err := http.ListenAndServe(fmt.Sprintf(":%s", port), nil) log.Fatal(err) } func process(w http.ResponseWriter, r *http.Request) { log.Println("Serving request") if r.Method == "GET" { fmt.Fprintln(w, "Ready to process POST requests from Cloud Storage trigger") return } // // 读取包含 Cloud Storage 对象元数据的请求正文 // gcsInputFile, err1 := readBody(r) if err1 != nil { log.Printf("Error reading POST data: %v", err1) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "Problem with POST data: %v \n", err1) return } // // 工作目录(并发安全) // localDir, errDir := ioutil.TempDir("", "") if errDir != nil { log.Printf("Error creating local temp dir: %v", errDir) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Could not create a temp directory on server. \n") return } defer os.RemoveAll(localDir) // // 从 Cloud Storage 下载输入文件 // localInputFile, err2 := download(gcsInputFile, localDir) if err2 != nil { log.Printf("Error downloading Cloud Storage file [%s] from bucket [%s]: %v", gcsInputFile.Name, gcsInputFile.Bucket, err2) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Error downloading Cloud Storage file [%s] from bucket [%s]", gcsInputFile.Name, gcsInputFile.Bucket) return } // // 使用 LibreOffice 将本地输入文件转换为本地 PDF 文件。 // localPDFFilePath, err3 := convertToPDF(localInputFile.Name(), localDir) if err3 != nil { log.Printf("Error converting to PDF: %v", err3) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Error converting to PDF.") return } // // 将新生成的 PDF 上传到 Cloud Storage // targetBucket := os.Getenv("PDF_BUCKET") err4 := upload(localPDFFilePath, targetBucket) if err4 != nil { log.Printf("Error uploading PDF file to bucket [%s]: %v", targetBucket, err4) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Error downloading Cloud Storage file [%s] from bucket [%s]", gcsInputFile.Name, gcsInputFile.Bucket) return } // // 从 Cloud Storage 删除原始输入文件。 // err5 := deleteGCSFile(gcsInputFile.Bucket, gcsInputFile.Name) if err5 != nil { log.Printf("Error deleting file [%s] from bucket [%s]: %v", gcsInputFile.Name, gcsInputFile.Bucket, err5) // 这不是阻止错误。 // 现在已成功生成并上传 PDF。 } log.Println("Successfully produced PDF") fmt.Fprintln(w, "Successfully produced PDF") } func convertToPDF(localFilePath string, localDir string) (resultFilePath string, err error) { log.Printf("Converting [%s] to PDF", localFilePath) cmd := exec.Command("libreoffice", "--headless", "--convert-to", "pdf", "--outdir", localDir, localFilePath) cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr log.Println(cmd) err = cmd.Run() if err != nil { return "", err } pdfFilePath := regexp.MustCompile(`\.\w+$`).ReplaceAllString(localFilePath, ".pdf") if !strings.HasSuffix(pdfFilePath, ".pdf") { pdfFilePath += ".pdf" } log.Printf("Converted %s to %s", localFilePath, pdfFilePath) return pdfFilePath, nil }
  4. 现在,运行以下命令来构建应用:

    go build -o server

    预期输出:

    go: downloading cloud.google.com/go/storage v1.6.0 go: downloading cloud.google.com/go v0.53.0 go: downloading github.com/googleapis/gax-go/v2 v2.0.5 go: downloading google.golang.org/api v0.18.0 go: downloading google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 go: downloading google.golang.org/grpc v1.27.1 go: downloading go.opencensus.io v0.22.3 go: downloading golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d go: downloading github.com/golang/protobuf v1.3.3 go: downloading golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 go: downloading github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e go: downloading golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae go: downloading golang.org/x/text v0.3.2

    该顶级代码所调用的函数位于源文件中:

    • server.go
    • notification.go
    • gcs.go

成功构建应用后,您就可以创建 PDF 转换服务了。

任务 3. 创建 PDF 转换服务

只要有文件上传到指定存储位置,该 PDF 服务就会使用 Cloud Run 和 Cloud Storage 来启动处理进程。

要做到这一点,您将使用一种常见模式的事件通知以及 Cloud Pub/Sub。这样做能使应用仅专注于处理信息。信息传输和传递工作则由其他服务来完成,从而使应用保持简单。

构建账单模块需要集成两个组件:

容器包含两个组件:server 和 LibreOffice

添加 LibreOffice 软件包后,您就可以在应用中使用该软件包。

  1. 打开编辑器中,打开现有的 Dockerfile 清单并更新该文件,如下所示:

    FROM amd64/debian RUN apt-get update -y \ && apt-get install -y libreoffice \ && apt-get clean WORKDIR /usr/src/app COPY server . CMD [ "./server" ]
  2. 保存更新后的 Dockerfile

  3. 使用 Cloud Build 启动对 PDF 转换器映像的重建:

    gcloud builds submit \ --tag gcr.io/$GOOGLE_CLOUD_PROJECT/pdf-converter

    点击检查我的进度,验证您是否完成了上述任务。

    使用 Cloud Build 创建映像
  4. 部署更新后的 PDF 转换器服务。

    注意:最好为 LibreOffice 提供 2GB RAM,请参阅带有 --memory 选项的一行。
  5. 运行以下命令,构建并部署容器:

    gcloud run deploy pdf-converter \ --image gcr.io/$GOOGLE_CLOUD_PROJECT/pdf-converter \ --platform managed \ --region {{{ project_0.default_region | "REGION" }}} \ --memory=2Gi \ --no-allow-unauthenticated \ --set-env-vars PDF_BUCKET=$GOOGLE_CLOUD_PROJECT-processed \ --max-instances=3

    点击检查我的进度,验证您是否完成了上述任务。

    已部署 PDF 转换器服务

Cloud Run 服务现已成功部署。但我们部署的 应用需要正确的权限才能访问。

任务 4. 创建服务账号

服务账号是一类能访问 Google API 的特殊账号。

在本实验中处理 Cloud Storage 事件时,使用服务账号访问 Cloud Run。Cloud Storage 支持一组丰富的通知,可用于触发事件。

接下来更新代码,以便在上传文件后通知应用。

  1. 依次点击导航菜单 > Cloud Storage,并验证是否已创建两个存储桶。您应该会看到:

    • -processed
    • -upload
  2. 创建一个 Pub/Sub 通知,以指示新文件已上传到文档存储桶(“uploaded”)。此类通知的主题带有“new-doc”标签。

    gsutil notification create -t new-doc -f json -e OBJECT_FINALIZE gs://$GOOGLE_CLOUD_PROJECT-upload

    预期输出:

    Created Cloud Pub/Sub topic projects/{{{project_0.project_id | "PROJECT_ID"}}}/topics/new-doc Created notification config projects/_/buckets/{{{project_0.project_id | "PROJECT_ID"}}}-upload/notificationConfigs/1
  3. 创建一个新的服务账号来触发 Cloud Run 服务:

    gcloud iam service-accounts create pubsub-cloud-run-invoker --display-name "PubSub Cloud Run Invoker"

    预期输出:

    Created service account [pubsub-cloud-run-invoker].
  4. 授予该服务账号调用 PDF 转换器服务的权限:

    gcloud run services add-iam-policy-binding pdf-converter \ --member=serviceAccount:pubsub-cloud-run-invoker@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \ --role=roles/run.invoker \ --region {{{ project_0.default_region | "REGION" }}} \ --platform managed

    预期输出:

    Updated IAM policy for service [pdf-converter]. bindings: - members: - serviceAccount:pubsub-cloud-run-invoker@{{{project_0.project_id | "PROJECT_ID"}}}.iam.gserviceaccount.com role: roles/run.invoker etag: BwYYfbXS240= version: 1
  5. 运行以下命令来查找您的项目编号:

    PROJECT_NUMBER=$(gcloud projects list \ --format="value(PROJECT_NUMBER)" \ --filter="$GOOGLE_CLOUD_PROJECT")
  6. 允许您的项目创建 Cloud Pub/Sub 身份验证令牌:

    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \ --member=serviceAccount:{{{ project_0.project_id | "PROJECT_ID" }}}@{{{ project_0.project_id | "PROJECT_ID" }}}.iam.gserviceaccount.com \ --role=roles/iam.serviceAccountTokenCreator

    点击检查我的进度,验证您是否完成了上述任务。

    服务账号已创建

服务账号创建后,即可用于调用 Cloud Run 服务。

任务 5. 测试 Cloud Run 服务

在进一步处理之前,测试已部署的服务。请记住,该服务需要身份验证,因此请进行测试,确保该服务的确是私有的。

  1. 在环境变量 $SERVICE_URL 中保存服务网址:

    SERVICE_URL=$(gcloud run services describe pdf-converter \ --platform managed \ --region {{{ project_0.default_region | "REGION" }}} \ --format "value(status.url)")
  2. 显示服务网址:

    echo $SERVICE_URL
  3. 向您的新服务发出匿名的 GET 请求:

    curl -X GET $SERVICE_URL

    预期输出:

    <html><head> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <title>403 Forbidden</title> </head> <body text=#000000 bgcolor=#ffffff> <h1>Error: Forbidden</h1> <h2>Your client does not have permission to get URL <code>/</code> from this server.</h2> <h2></h2> 注意: 该匿名的 GET 请求会导致出现以下错误消息: “您的客户端没有权限,无法获取网址”。 这是正常的,因为您不会希望用户匿名调用该服务。
  4. 现在尝试以授权用户的身份调用该服务:

    curl -X GET -H "Authorization: Bearer $(gcloud auth print-identity-token)" $SERVICE_URL

    预期输出

    已准备好从 Cloud Storage 触发器处理 POST 请求

太棒了!您已成功部署一项进行了身份验证的 Cloud Run 服务。

任务 6. Cloud Storage 触发器

要在有新内容上传到 Cloud Storage 时启动通知,应为现有的 Pub/Sub 主题添加订阅。

注意:在新内容上传时,Cloud Storage 通知将自动向您的主题队列推送消息。通过使用通知,您可以创建功能强大的应用,在无需额外编写代码的情况下即可响应事件。
  • 创建一个 Pub/Sub 订阅,这样只要有消息发布到主题 new-doc,PDF 转换器就会运行:

    gcloud pubsub subscriptions create pdf-conv-sub \ --topic new-doc \ --push-endpoint=$SERVICE_URL \ --push-auth-service-account=pubsub-cloud-run-invoker@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com

    预期输出:

    Created subscription [projects/{{{ project_0.project_id| "PROJECT_ID" }}}/subscriptions/pdf-conv-sub].

    点击检查我的进度,验证您是否完成了上述任务。

    确认 Pub/Sub 订阅

现在,只要上传了文件,Pub/Sub 订阅就会与您的服务账号进行互动。然后,服务账号会启动 PDF 转换器 Cloud Run 服务。

任务 7. 测试 Cloud Storage 通知

要测试 Cloud Run 服务,请使用所提供的样本文件。

  1. 将测试文件复制到上传存储桶中:

    gsutil -m cp -r gs://spls/gsp762/* gs://$GOOGLE_CLOUD_PROJECT-upload

    预期输出:

    Copying gs://spls/gsp762/cat-and-mouse.jpg [Content-Type=image/jpeg]... Copying gs://spls/gsp762/file-sample_100kB.doc [Content-Type=application/msword]... Copying gs://spls/gsp762/file-sample_500kB.docx [Content-Type=application/vnd.openxmlformats-officedocument.wordprocessingml.document]... Copying gs://spls/gsp762/file_example_XLS_10.xls [Content-Type=application/vnd.ms-excel]... Copying gs://spls/gsp762/file-sample_1MB.docx [Content-Type=application/vnd.openxmlformats-officedocument.wordprocessingml.document]... Copying gs://spls/gsp762/file_example_XLSX_50.xlsx [Content-Type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet]... Copying gs://spls/gsp762/file_example_XLS_100.xls [Content-Type=application/vnd.ms-excel]... Copying gs://spls/gsp762/file_example_XLS_50.xls [Content-Type=application/vnd.ms-excel]... Copying gs://spls/gsp762//Copy of cat-and-mouse.jpg [Content-Type=image/jpeg]...
  2. 在 Cloud 控制台中,依次点击 Cloud Storage > 存储桶,然后点击名称以“-upload”结尾的存储桶

  3. 点击刷新按钮几次,查看文件在转换为 PDF 后,是如何逐一删除原始文件的。

  4. 然后点击存储桶,再点击名称以“-processed”结尾的存储桶。该存储桶应包含所有文件的 PDF 版本。

    注意: 文件处理可能需要几分钟时间。 使用存储桶刷新选项来检查处理完成状态。
  5. 请打开 PDF 文件,确保它们已正确转换。

  6. 上传完成后,依次点击导航菜单 > Cloud Run,然后点击 PDF 转换器服务。

  7. 选择日志标签页并添加一个“转换”过滤器,以便查看已转换的文件。

  8. 前往导航菜单 > Cloud Storage 并打开名称以“-upload”结尾的存储桶,确认所有上传的文件都已处理。

很棒!您已成功构建一项新服务,即使用上传到 Cloud Storage 的文件来创建 PDF 文件。

恭喜!

在本实验中,您学习了如何将 Go 应用转换为容器,以及如何利用 Google Cloud Build 构建容器,从而启用了一项 Cloud Run 服务。

您还掌握了通过服务账号启用权限和利用 Cloud Storage 事件处理的技能,这些都是 PDF 转换器服务运行不可或缺的一部分,该服务可将文档转换为 PDF 文件并存储到“已处理”存储桶中。

Google Cloud 培训和认证

…可帮助您充分利用 Google Cloud 技术。我们的课程会讲解各项技能与最佳实践,可帮助您迅速上手使用并继续学习更深入的知识。我们提供从基础到高级的全方位培训,并有点播、直播和虚拟三种方式选择,让您可以按照自己的日程安排学习时间。各项认证可以帮助您核实并证明您在 Google Cloud 技术方面的技能与专业知识。

上次更新手册的时间:2024 年 5 月 15 日

上次测试实验的时间:2024 年 5 月 15 日

版权所有 2025 Google LLC 保留所有权利。Google 和 Google 徽标是 Google LLC 的商标。其他所有公司名和产品名可能是其各自相关公司的商标。

准备工作

  1. 实验会创建一个 Google Cloud 项目和一些资源,供您使用限定的一段时间
  2. 实验有时间限制,并且没有暂停功能。如果您中途结束实验,则必须重新开始。
  3. 在屏幕左上角,点击开始实验即可开始

使用无痕浏览模式

  1. 复制系统为实验提供的用户名密码
  2. 在无痕浏览模式下,点击打开控制台

登录控制台

  1. 使用您的实验凭证登录。使用其他凭证可能会导致错误或产生费用。
  2. 接受条款,并跳过恢复资源页面
  3. 除非您已完成此实验或想要重新开始,否则请勿点击结束实验,因为点击后系统会清除您的工作并移除该项目

此内容目前不可用

一旦可用,我们会通过电子邮件告知您

太好了!

一旦可用,我们会通过电子邮件告知您

一次一个实验

确认结束所有现有实验并开始此实验

使用无痕浏览模式运行实验

请使用无痕模式或无痕式浏览器窗口运行此实验。这可以避免您的个人账号与学生账号之间发生冲突,这种冲突可能导致您的个人账号产生额外费用。