十分钟上线-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图片处理服务

相关实践学习
【AI破次元壁合照】少年白马醉春风,函数计算一键部署AI绘画平台
本次实验基于阿里云函数计算产品能力开发AI绘画平台,可让您实现“破次元壁”与角色合照,为角色换背景效果,用AI绘图技术绘出属于自己的少年江湖。
从 0 入门函数计算
在函数计算的架构中,开发者只需要编写业务代码,并监控业务运行情况就可以了。这将开发者从繁重的运维工作中解放出来,将精力投入到更有意义的开发任务上。
目录
相关文章
|
5月前
|
Serverless Python
借助 serverless 将 MCP 服务部署到云端
本文介绍了如何将 MCP 服务通过 SSE 协议部署到云端,避免本地下载和启动的麻烦。首先,使用 Python 实现了一个基于 FastMCP 的网络搜索工具,并通过设置 `transport='sse'` 启用 SSE 协议。接着,编写客户端代码测试服务功能,确保其正常运行。随后,利用阿里云函数计算服务(FC 3.0)以 Serverless 方式部署该服务,包括创建函数、配置环境变量、添加依赖层以及部署代码。最后,提供了客户端测试方法和日志排查技巧,并展示了如何在不同工具(如 Cherry-Studio、Cline 和 Cursor)中配置云端 MCP 服务。
1051 11
借助 serverless 将 MCP 服务部署到云端
|
11月前
|
存储 Serverless 数据库
科普文:云计算服务类型IaaS, PaaS, SaaS, BaaS, Faas说明
本文介绍了云计算服务的几种主要类型,包括IaaS(基础设施即服务)、PaaS(平台即服务)、SaaS(软件即服务)、BaaS(后端即服务)和FaaS(函数即服务)。每种服务模式提供了不同的服务层次和功能,从基础设施的提供到应用的开发和运行,再到软件的交付使用,满足了企业和个人用户在不同场景下的需求。文章详细阐述了每种服务模式的特点、优势和缺点,并列举了相应的示例。云计算服务的发展始于21世纪初,随着互联网技术的普及,这些服务模式不断演进,为企业和个人带来了高效、灵活的解决方案。然而,使用这些服务时也需要注意服务的稳定性、数据安全性和成本等问题。
7724 5
|
7月前
|
Cloud Native 安全 Serverless
云原生应用实战:基于阿里云Serverless的API服务开发与部署
随着云计算的发展,Serverless架构日益流行。阿里云函数计算(Function Compute)作为Serverless服务,让开发者无需管理服务器即可运行代码,按需付费,简化开发运维流程。本文从零开始,介绍如何使用阿里云函数计算开发简单的API服务,并探讨其核心优势与最佳实践。通过Python示例,演示创建、部署及优化API的过程,涵盖环境准备、代码实现、性能优化和安全管理等内容,帮助读者快速上手Serverless开发。
|
Cloud Native 关系型数据库 Serverless
基于阿里云函数计算(FC)x 云原生 API 网关构建生产级别 LLM Chat 应用方案最佳实践
本文带大家了解一下如何使用阿里云Serverless计算产品函数计算构建生产级别的LLM Chat应用。该最佳实践会指导大家基于开源WebChat组件LobeChat和阿里云函数计算(FC)构建企业生产级别LLM Chat应用。实现同一个WebChat中既可以支持自定义的Agent,也支持基于Ollama部署的开源模型场景。
1591 115
|
10月前
|
弹性计算 人工智能 自然语言处理
魔搭社区与函数计算:高效部署开源大模型的文本生成服务体验
在数字化时代,人工智能技术迅速发展,开源大模型成为重要成果。魔搭社区(ModelScope)作为开源大模型的聚集地,结合阿里云函数计算,提供了一种高效、便捷的部署方式。通过按需付费和弹性伸缩,开发者可以快速部署和使用大模型,享受云计算的便利。本文介绍了魔搭社区与函数计算的结合使用体验,包括环境准备、部署应用、体验使用和资源清理等步骤,并提出了改进建议。
|
11月前
|
机器学习/深度学习 监控 物联网
函数即服务(FaaS)
函数即服务(FaaS)
543 6
|
12月前
|
弹性计算 关系型数据库 Serverless
告别资源瓶颈,函数计算驱动多媒体文件处理方案:https://www.aliyun.com/solution/tech-solution/fc-drive-file
本文介绍了一种基于阿里云的一键部署解决方案,利用云服务器ECS、RDS MySQL、OSS、函数计算FC及MNS等服务,实现高效的多媒体文件处理。方案通过事件驱动机制,将文件处理任务解耦,并自动弹性扩展,按需付费,简化部署流程,提高处理效率。本文还提供了详细的部署步骤与体验反馈,展示了从配置到文件处理的全过程。
|
5月前
|
SQL 分布式计算 Serverless
鹰角网络:EMR Serverless Spark 在《明日方舟》游戏业务的应用
鹰角网络为应对游戏业务高频活动带来的数据潮汐、资源弹性及稳定性需求,采用阿里云 EMR Serverless Spark 替代原有架构。迁移后实现研发效率提升,支持业务快速发展、计算效率提升,增强SLA保障,稳定性提升,降低运维成本,并支撑全球化数据架构部署。
505 56
鹰角网络:EMR Serverless Spark 在《明日方舟》游戏业务的应用
|
3月前
|
存储 编解码 Serverless
Serverless架构下的OSS应用:函数计算FC自动处理图片/视频转码(演示水印添加+缩略图生成流水线)
本文介绍基于阿里云函数计算(FC)和对象存储(OSS)构建Serverless媒体处理流水线,解决传统方案资源利用率低、运维复杂、成本高等问题。通过事件驱动机制实现图片水印添加、多规格缩略图生成及视频转码优化,支持毫秒级弹性伸缩与精确计费,提升处理效率并降低成本,适用于高并发媒体处理场景。
201 0
|
5月前
|
人工智能 开发框架 安全
Serverless MCP 运行时业界首发,函数计算让 AI 应用最后一公里提速
作为云上托管 MCP 服务的最佳运行时,函数计算 FC 为阿里云百炼 MCP 提供弹性调用能力,用户只需提交 npx 命令即可“零改造”将开源 MCP Server 部署到云上,函数计算 FC 会准备好计算资源,并以弹性、可靠的方式运行 MCP 服务,按实际调用时长和次数计费,欢迎你在阿里云百炼和函数计算 FC 上体验 MCP 服务。
489 30

热门文章

最新文章

相关产品

  • 函数计算