十分钟上线-FC&IMM构建serverless文档转换/预览服务

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
函数计算FC,每月15万CU 3个月
简介: 自从人类进入信息时代以来,办公文档是每个人日常频繁使用的工具,ppt、word、xls、wps、pdf 等为我们工作和生活带来了很多的便利,尤其进入云计算和移动互联网时代,人们可以利用各种终端来预览 ppt、word、pdf 等相关文档进行工作和学习,这种情况下,文档之间的格式转换,各种终端的适配预览显得尤为重要,在本文中,我们来看看阿里云函数计算和智能媒体管理相结合,快速实现一个弹性高可用的文档转换/预览服务。

前言

自从人类进入信息时代以来,办公文档是每个人日常频繁使用的工具,ppt、word、xls、wps、pdf 等为我们工作和生活带来了很多的便利,尤其进入云计算和移动互联网时代,人们可以利用各种终端来预览 ppt、word、pdf 等相关文档进行工作和学习,这种情况下,文档之间的格式转换,各种终端的适配预览显得尤为重要,在本文中,我们通过一个ppt 转换成 vector 并且进行预览的例子,来看看阿里云函数计算和智能媒体管理相结合,快速实现一个弹性高可用的文档转换/预览服务。

FC&IMM 文档预览体验入口地址:
http://fcdemo.mofangdegisn.cn/fc-imm-demo

您可以使用不同的设备(比如pc,ipad, 手机等)打开上面这个链接,预览效果很好,可以很好地适配各种终端

函数计算

阿里云函数计算是一个事件驱动的serverless计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,具体表现为:

  • 无需采购和管理服务器等基础设施
  • 按需付费
  • 专注业务逻辑的开发,能极大提高开发效率
  • 稳定高可用,毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力
  • 提供日志查询、性能监控、报警等功能快速排查故障

智能媒体管理

阿里云智能媒体管理(Intelligent Media Management,简称 IMM),是阿里云提供的针对媒体数据的高级、智能管理服务。它具有与平台无关的 RESTful API 接口,为阿里云上的非结构化存储数据(例如,OSS 中的视频、图片、文档等数据)提供快捷的数据处理通道,比如 OFFICE 格式转换,图片、视频的编辑处理,以及人工智能的价值数据提取和检索(例如,标签识别、人脸分组)。IMM 提供场景化构建的一站式数据应用解决方案,适合媒资管理、智能网盘、社交应用、图库图床等开发者使用。

在本文中,我们主要讨论智能媒体管理产品文档转换/预览功能

弹性高可用的文档转换/预览服务快速构建

在本案例中,我们对一个ppt文档转换成vector格式,然后直接使用“智能媒体管理”服务提供的预览渲染引擎, 对转换成功后的文件进行预览。本文只针对ppt 转换成 vector,智能媒体支持十分丰富的文档转换,具体参考:https://help.aliyun.com/document_detail/63761.html

在案例中,我们使用的阿里云资源在同一个region-杭州

1. 开通函数计算智能媒体管理和对象存储

2. 登录智能媒体管理控制台),创建一个project,假设为 imm-demo

image

3. 登录对象存储控制台, 创建一个bucket,假设为 fc-imm-demo

  • 由于要浏览器进行预览,这里设置跨域访问配置 *.imm.aliyun.com
    image
  • 新建一个测试目录,上传测试ppt文件测试文件到指定目录
    image

4. 登录函数计算控制台, 创建相应的service 和 function, 具体参考使用控制台编写函数

  • 配置service的role 具有访问imm和oss读的权限

    配置oss读权限主要是为了生成安全预览文档的url地址,在本文中直接授予service 的role 具有 AliyunIMMFullAccessAliyunOSSReadOnlyAccess, 当然为了安全,您可以把oss读权限的粒度具体到某个 bucket 和某个文件夹。
    如果您不想使用函数计算中 service 中的 role 参与生成预览url, 您可以直接创建单独的子账号,在函数调用ram相关服务获取sts,过程可以参考:https://yq.aliyun.com/articles/589902

    image

  • 编写函数, 函数计算 python/nodejs runtime 内置了 imm 的 sdk,直接编写代码, php runtime 将代码和函数实现文件一起打包(可以在附件直接下载代码 zip 包fc-imm-php.zip),具体可以参考 PHP runtime 使用自定义库

代码如下:

python 版本

#-*- coding: utf8 -*-

import json, time
from collections import OrderedDict
from aliyunsdkcore.client import AcsClient
from aliyunsdkimm.request.v20170906 import CreateOfficeConversionTaskRequest
from aliyunsdkimm.request.v20170906 import GetOfficeConversionTaskRequest
from aliyunsdkcore.auth.credentials import StsTokenCredential

try:
   from urllib import quote
except:
   from urllib.parse import quote

PREVIEWURL = 'https://preview.imm.aliyun.com/index.html';
PREVIEWTGTPATH = 'fc-demo-preview-output-py';

immProject = "imm-demo";
srcUri = "oss://fc-imm-demo-sz/test-data/office/test.pptx";
# https://help.aliyun.com/document_detail/63761.html
# https://help.aliyun.com/document_detail/74947.html
# 使用前端渲染引擎预览,这里的type必须是vector
tgtType = "vector"

def getBucketByUri(uri):
  if uri.startswith("oss://"):
    return uri[6:].split("/")[0]
  raise Exception("SrcUri is invalid")

def getFileNameByUri(uri):
  return uri.split("/")[-1]

def handler(event, context):
  creds = context.credentials
  sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
  clt = AcsClient(region_id=context.region, credential=sts_token_credential)

  bucket = getBucketByUri(srcUri)
  fileName = getFileNameByUri(srcUri)

  tgtUri = "oss://{0}/{1}/{2}".format(bucket, PREVIEWTGTPATH, fileName)
  createReq = CreateOfficeConversionTaskRequest.CreateOfficeConversionTaskRequest()
  createReq.set_Project(immProject)
  createReq.set_SrcUri(srcUri)
  createReq.set_TgtUri(tgtUri)
  createReq.set_TgtType(tgtType)
  response = clt.do_action_with_exception(createReq)
  print(response)

  res = json.loads(response)
  taskId = res["TaskId"]
  getReq = GetOfficeConversionTaskRequest.GetOfficeConversionTaskRequest()
  getReq.set_Project(immProject)
  getReq.set_TaskId(taskId)
  period = 1
  timeout = 30
  start = time.time()
  while True:
      response = clt.do_action_with_exception(getReq)
      #print(response)
      status = json.loads(response)["Status"]
      if status == "Finished":    #任务完成
          print("Task finished.")
          break
      if status == "Failed":     #任务失败
          print("Task failed.")
          break
      if time.time() - start > timeout:   #任务超时
          print("Task timeout.")
          break
      time.sleep(period)

  # get preview url
  params = OrderedDict()
  params["url"] = "http://" + context.region + ".aliyuncs.com/" + PREVIEWTGTPATH + "/"  + fileName
  params["accessKeyId"] = creds.access_key_id
  params["accessKeySecret"] = creds.access_key_secret
  params["stsToken"] = quote(creds.security_token)
  params["region"] = "oss-" + context.region
  params["bucket"] = bucket
  paramsLi = [ k + "=" + v  for k, v in params.items()]
  paramsStr = "&" .join(paramsLi)
  preview = PREVIEWURL + "?" + paramsStr

  print(preview)

  return preview

nodejs 版本

const {
    RPCClient } = require('@alicloud/pop-core');

function getBucketByUri(uri) {
   
  if(uri.startsWith("oss://")){
   
    return uri.substr(6).split("/")[0];
  }
  throw new Exception("SrcUri is invalid");
}

function getFileNameByUri(uri) {
   
  var arr = uri.split("/");
  return arr[arr.length - 1];
}

 function parse(params){
   
    return Object.keys(params).map(function (key){
   
      return `${
     key}=${
     params[key]}`;
    }).join("&");
}

exports.handler = function(event, context, callback) {
   
  // 预览引擎的访问地址
  const previewUrl = 'https://preview.imm.aliyun.com/index.html';
  // 转换结果存放路径
  const previewTgtPath = 'fc-demo-preview-js';

  var immProject = "imm-demo";
  var immSrcUri = "oss://fc-imm-demo/test-data/office/test.pptx";

  // 获取AK信息
  var akInfo = {
   
    accessKeyId: context.credentials.accessKeyId,
    accessKeySecret: context.credentials.accessKeySecret,
    securityToken: context.credentials.securityToken
  };

  var bucket = getBucketByUri(immSrcUri)

  var client = new RPCClient({
   
    endpoint: `https://imm.${
     context.region}.aliyuncs.com`,
    accessKeyId: akInfo.accessKeyId,
    accessKeySecret: akInfo.accessKeySecret,
    securityToken: akInfo.securityToken,
    apiVersion: '2017-09-06'
  });

  var fileName = getFileNameByUri(immSrcUri);

  // https://help.aliyun.com/document_detail/63761.html
  // https://help.aliyun.com/document_detail/74947.html
 // 使用前端渲染引擎预览,这里的type必须是vector
  var immParams = {
   
    Project: immProject,
    SrcUri: immSrcUri,
    TgtType: "vector",
    TgtUri: `oss://${
     bucket}/${
     previewTgtPath}/${
     fileName}`
  };

  var ossRegion = `oss-${
     context.region}`

  //get vector  preview url
  var params = {
   };
  // 预览文档地址
  params.url = `http://${
     bucket}.${
     ossRegion}.aliyuncs.com/${
     previewTgtPath}/${
     fileName}`;
  params.accessKeyId = akInfo.accessKeyId;
  // 访问预览文档的accessKeySecret
  params.accessKeySecret = akInfo.accessKeySecret;
  // 访问预览文档的SecurityToken
  params.stsToken = encodeURIComponent(akInfo.securityToken);
  // 预览文档的region
  params.region = ossRegion;
  // 预览文档的bucket
  params.bucket = bucket;
  // 拼接预览URL
  var url = `${
     previewUrl}?${
     parse(params)}`;

  client.request("createOfficeConversionTask", immParams).then(function() {
   
    console.log(url);
    callback(null, url); 
  }); 
};

php 版本

<?php
require_once __DIR__ . '/aliyun-openapi-php-sdk/aliyun-php-sdk-core/Config.php';
use imm\Request\V20170906 as Imm;

define('PREVIEWURL', 'https://preview.imm.aliyun.com/index.html');

function myErrorHandler($errno, $errstr, $errfile, $errline) {
   
    if (!(error_reporting() & $errno)) {
   
        return false;
    }

    switch ($errno) {
   
    case E_USER_ERROR:
        $errInfo = array(
            "errorMessage" => $errstr,
            "errorType"    => \ServerlessFC\friendly_error_type($errno),
            "stackTrace"   => array(
                "file" => $errfile,
                "line" => $errline,
            ),
        );
        throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
        break;

    default: // E_USER_WARNING | E_USER_NOTICE
        break;
    }

    /* Don't execute PHP internal error handler */
    return true;
}

// set to the user defined error handler
set_error_handler("myErrorHandler");

function startsWith($haystack, $needle) {
   
    $length = strlen($needle);
    return (substr($haystack, 0, $length) === $needle);
}

function getBucketByUri($uri) {
   
    if (startsWith($uri, "oss://")) {
   
        $tmp = substr($uri, 6);
        return explode("/", $tmp)[0];
    }
    throw new Exception("SrcUri is invalid");
}

function getFileNameByUri($uri) {
   
    $tmp = explode("/", $uri);
    return $tmp[count($tmp) - 1];
}

function handler($event, $context) {
   
    $accessKeyId     = $context["credentials"]["accessKeyId"];
    $accessKeySecret = $context["credentials"]["accessKeySecret"];
    $securityToken   = $context["credentials"]["securityToken"];
    $region          = $context['region'];

    $iClientProfile = DefaultProfile::getProfile(
        $region,
        $accessKeyId,
        $accessKeySecret,
        $securityToken
    );

    $client         = new DefaultAcsClient($iClientProfile);
    $PREVIEWTGTPATH = 'fc-demo-preview-output-php';
    $immProject     = "imm-demo";
    $srcUri         = "oss://fc-imm-demo/test-data/office/test.pptx";

    $bucket   = getBucketByUri($srcUri);
    $fileName = getFileNameByUri($srcUri);
    $tgtUri   = sprintf("oss://%s/%s/%s", $bucket, $PREVIEWTGTPATH, $fileName);

    $request = new Imm\CreateOfficeConversionTaskRequest();
    $request->setProject($immProject);
    $request->setSrcUri($srcUri);
    $request->setTgtType("vector");
    $request->setTgtUri($tgtUri);
    $response = $client->getAcsResponse($request);
    print_r($response);

    $maxRetryCount = 30;
    $retryDelay    = 1;
    $request       = new Imm\GetOfficeConversionTaskRequest();
    $request->setTaskId($response->TaskId);
    $request->setProject($immProject);
    while ($maxRetryCount--) {
   
        $response = $client->getAcsResponse($request);
        print_r($response);
        if ($response->Status != 'Running') {
   
            break;
        }
        sleep($retryDelay);
    }

    // get preview url
    $params                    = array();
    $params["url"]             = "http://" . $region . ".aliyuncs.com/" . $PREVIEWTGTPATH . "/" . $fileName;
    $params["accessKeyId"]     = $accessKeyId;
    $params["accessKeySecret"] = $accessKeySecret;
    $params["stsToken"]        = rawurlencode($securityToken);
    $params["region"]          = "oss-" . $region;
    $params["bucket"]          = $bucket;

    $paramsLi = [];
    foreach ($params as $key => $value) {
   
        $paramsLi[] = $key . "=" . $value;
    }
    $paramsStr = implode("&", $paramsLi);

    $preview = PREVIEWURL . "?" . $paramsStr;

    echo $preview . PHP_EOL;

    return $preview;
}

5. 点击执行函数,函数的返回值为预览的url,用浏览器打开生成的url,就可以预览转换格式后的ppt了

image

总结

从本文的示例中,我们可以看出,基于oss存储,使用函数计算和智能媒体管理,短短的几行代码就可以快速实现一个弹性高可用的文档转换/预览服务,在本文中,演示的函数的触发是通过手动触发,函数计算集成丰富的事件源,通过事件源可以触发函数,比如对于上传到oss的某个文档,自动触发文档格式转换, 结合 http trigger (比如上面的体验的入口地址) 来设置 event 来达到对主动对某个文档格式转换并直接预览,从而实现实现一个弹性高可用的文档转换/预览服务,类似函数计算实现Serverless图片处理服务

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
4天前
|
人工智能 运维 物联网
云大使 X 函数计算 FC 专属活动上线!享返佣,一键打造 AI 应用
如今,AI 技术已经成为推动业务创新和增长的重要力量。但对于许多企业和开发者来说,如何高效、便捷地部署和管理 AI 应用仍然是一个挑战。阿里云函数计算 FC 以其免运维的特点,大大降低了 AI 应用部署的复杂性。用户无需担心底层资源的管理和运维问题,可以专注于应用的创新和开发,并且用户可以通过一键部署功能,迅速将 AI 大模型部署到云端,实现快速上线和迭代。函数计算目前推出了多种规格的云资源优惠套餐,用户可以根据实际需求灵活选择。
|
5天前
|
人工智能 运维 Serverless
云端问道8期方案教学-基于Serverless计算快速构建AI应用开发
本文介绍了基于Serverless计算快速构建AI应用开发的技术和实践。内容涵盖四个方面:1) Serverless技术价值,包括其发展趋势和优势;2) Serverless函数计算与AI的结合,探讨AIGC应用场景及企业面临的挑战;3) Serverless函数计算AIGC应用方案,提供一键部署、模型托管等功能;4) 业务初期如何低门槛使用,介绍新用户免费额度和优惠活动。通过这些内容,帮助企业和开发者更高效地利用Serverless架构进行AI应用开发。
|
5月前
|
消息中间件 运维 Serverless
函数计算产品使用问题之如何部署Stable Diffusion Serverless API
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
30天前
|
人工智能 Serverless API
尽享红利,Serverless构建企业AI应用方案与实践
本次课程由阿里云云原生架构师计缘分享,主题为“尽享红利,Serverless构建企业AI应用方案与实践”。课程分为四个部分:1) Serverless技术价值,介绍其发展趋势及优势;2) Serverless函数计算与AI的结合,探讨两者融合的应用场景;3) Serverless函数计算AIGC应用方案,展示具体的技术实现和客户案例;4) 业务初期如何降低使用门槛,提供新用户权益和免费资源。通过这些内容,帮助企业和开发者快速构建高效、低成本的AI应用。
74 12
|
1月前
|
运维 Serverless 测试技术
通义灵码 x 函数计算:构建高效开发流程,加速项目交付
本方案基于通义大模型的通义灵码,提供代码生成、补全、优化及单元测试生成等能力,提升编码效率和质量。结合云效和函数计算 FC 进行代码管理、持续集成、部署发布,加速项目交付,为开发者提供智能编码、CI/CD、部署上线体验,加快产品迭代速度。
|
3月前
|
机器学习/深度学习 监控 Serverless
无服务器架构(Serverless)
无服务器架构(Serverless)
119 4
|
4月前
|
Cloud Native 关系型数据库 Serverless
基于阿里云函数计算(FC)x 云原生 API 网关构建生产级别 LLM Chat 应用方案最佳实践
本文带大家了解一下如何使用阿里云Serverless计算产品函数计算构建生产级别的LLM Chat应用。该最佳实践会指导大家基于开源WebChat组件LobeChat和阿里云函数计算(FC)构建企业生产级别LLM Chat应用。实现同一个WebChat中既可以支持自定义的Agent,也支持基于Ollama部署的开源模型场景。
908 30
|
5月前
|
存储 运维 Serverless
Serverless 支撑赛事转播问题之利用函数计算实现图片处理的实时性和成本节约如何解决
Serverless 支撑赛事转播问题之利用函数计算实现图片处理的实时性和成本节约如何解决
|
5月前
|
数据可视化 NoSQL Serverless
现代化 Web 应用构建问题之Serverless架构的Web站点费用计算如何解决
现代化 Web 应用构建问题之Serverless架构的Web站点费用计算如何解决
59 1
|
5月前
|
消息中间件 运维 Serverless
Serverless 支撑赛事转播问题之利用函数计算处理视频直播截帧服务如何解决
Serverless 支撑赛事转播问题之利用函数计算处理视频直播截帧服务如何解决

相关产品

  • 函数计算