{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2020-09-09-jiangqizheng-art","result":{"data":{"currentBlog":{"id":"559c2b50-dcca-543e-aa11-a4b77232fd22","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/202099/1599651282704-v2-24a20fb94b5d74840d043f4cd2bd40e3_r.jpg","authors":["蒋启钲"],"categories":["user-stories"],"date":"2020-09-09T00:00:00.000Z","title":"用 Serverless 优雅地实现图片艺术化应用","description":"本文将会分享，如何从零开始搭建一个基于腾讯云 Serverless 的图片艺术化应用！","authorslink":["https://zhuanlan.zhihu.com/p/218803108"],"translators":null,"translatorslink":null,"tags":["Serverless","TensorFlow"],"keywords":"Serverless, Serverless Framework, Serverless 实践","outdated":null},"wordCount":{"words":445,"sentences":111,"paragraphs":110},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-09-09-jiangqizheng-art.md","fields":{"slug":"/blog/2020-09-09-jiangqizheng-art/","keywords":["go","koa","next.js","nextjs","nodejs","python","react","serverless","webpack","website","云函数","node","layer"]},"html":"<p>本文将会分享，如何从零开始搭建一个基于腾讯云 Serverless 的图片艺术化应用！</p>\n<blockquote>\n<p>线上 demo 预览：<a href=\"https://art.x96.xyz/\">https://art.x96.xyz/</a> ，项目已开源，完整代码见文末。</p>\n</blockquote>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651083623-v2-74aee01635eec12eea32cf2ae80d4ceb_b.png\" alt=\"img\"></p>\n<p>在完整阅读文章后，读者应该能够实现并部署一个相同的应用，这也是本篇文章的目标。</p>\n<p><strong>项目看点概览：</strong></p>\n<ul>\n<li>前端 react（Next.js）、后端 node（koa2）</li>\n<li>全面使用 ts 进行开发，极致开发体验（后端运行时 ts 的方案，虽然性能差点，不过胜在无需编译，适合写 demo）</li>\n<li>突破云函数代码 500mb 限制（提供解决方案）</li>\n<li>TensorFlow2 + Serverless 扩展想象力边际</li>\n<li>高性能，轻松应对万级高并发，实现高可用（自信的表情，反正是平台干的活）</li>\n<li>秒级部署，十秒部署上线</li>\n<li>开发周期短（本文就能带你完成开发）</li>\n</ul>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651387973-v2-1fde91eae8d502af43bde5912e7a55aa_b.png\" alt=\"img\"></p>\n<p>本项目部署借助了 Serverless component，因此当前开发环境需先全局安装 Serverless 命令行工具</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"17707085082434992000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`npm install -g serverless`, `17707085082434992000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> -g serverless</code></pre></div>\n<h2 id=\"需求与架构\"><a href=\"#%E9%9C%80%E6%B1%82%E4%B8%8E%E6%9E%B6%E6%9E%84\" aria-label=\"需求与架构 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>需求与架构</h2>\n<p>本应用的整体需求很简单：图片上传与展示。</p>\n<ol>\n<li>模块概览</li>\n</ol>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651426241-v2-503298ca435eee806d6ad281505e5db2_b.png\" alt=\"模块概览\"></p>\n<ol start=\"2\">\n<li>上传图片</li>\n</ol>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651443287-v2-1f5d371818671e0122d57650da860765_b.png\" alt=\"上传图片\"></p>\n<ol start=\"3\">\n<li>浏览图片 </li>\n</ol>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651463965-v2-a51d31aa252e42d2f073a1ee69cb42d1_b.png\" alt=\"浏览图片\"></p>\n<h2 id=\"用对象存储提供存储服务\"><a href=\"#%E7%94%A8%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8%E6%8F%90%E4%BE%9B%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1\" aria-label=\"用对象存储提供存储服务 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>用对象存储提供存储服务</h2>\n<p>在开发之前，我们先创建一个 oss 用于提供图片存储（可以使用你已有的对象存储）</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"99421419288594090000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`mkdir oss`, `99421419288594090000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> oss</code></pre></div>\n<p>在新建的 oss 目录下添加 serverless.yml </p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"53472093045859490000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`component: cos\nname: xart-oss\napp: xart\nstage: dev\n\ninputs:\n  src:\n    src: ./\n    exclude:\n      - .env # 防止密钥被上传\n  bucket: \\${name} # 存储桶名称，如若不添加 AppId 后缀，则系统会自动添加，后缀为大写（xart-oss-<你的appid>）\n  website: false\n  targetDir: /\n  protocol: https\n  region: ap-guangzhou # 配置区域，尽量配置在和服务同区域内，速度更快\n  acl:\n    permissions: public-read # 读写配置为，私有写，共有读`, `53472093045859490000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> cos\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> xart<span class=\"token punctuation\">-</span>oss\n<span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> xart\n<span class=\"token key atrule\">stage</span><span class=\"token punctuation\">:</span> dev\n\n<span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span> ./\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env <span class=\"token comment\"># 防止密钥被上传</span>\n  <span class=\"token key atrule\">bucket</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>name<span class=\"token punctuation\">}</span> <span class=\"token comment\"># 存储桶名称，如若不添加 AppId 后缀，则系统会自动添加，后缀为大写（xart-oss-&lt;你的appid>）</span>\n  <span class=\"token key atrule\">website</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">false</span>\n  <span class=\"token key atrule\">targetDir</span><span class=\"token punctuation\">:</span> /\n  <span class=\"token key atrule\">protocol</span><span class=\"token punctuation\">:</span> https\n  <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>guangzhou <span class=\"token comment\"># 配置区域，尽量配置在和服务同区域内，速度更快</span>\n  <span class=\"token key atrule\">acl</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">permissions</span><span class=\"token punctuation\">:</span> public<span class=\"token punctuation\">-</span>read <span class=\"token comment\"># 读写配置为，私有写，共有读</span></code></pre></div>\n<p>执行 sls deploy 几秒后，你应该就能看到如下提示，表示新建对象存储成功。</p>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651487608-v2-fb1abe3f51ae4aa8f6246bec14d3a786_b.png\" alt=\"新建对象存储\"></p>\n<p>这里，我们看到 <code class=\"language-text\">url</code> <a href=\"https://art-oss-\">https://art-oss-</a><appid>.cos.ap-guangzhou.myqcloud.com，可以发现默认的命名规则是 https://&#x3C;名字-appid>.cos.&#x3C;地域>.myqcloud.com</p>\n<p>简单记录一下，在后面服务中会用到，忘记了也不要紧，看看 <code class=\"language-text\">.env</code> 内 <code class=\"language-text\">TENCENT_APP_ID</code> 字段（部署后会自动生成 .env）</p>\n<h2 id=\"实现后端服务\"><a href=\"#%E5%AE%9E%E7%8E%B0%E5%90%8E%E7%AB%AF%E6%9C%8D%E5%8A%A1\" aria-label=\"实现后端服务 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>实现后端服务</h2>\n<p>新建一个目录并初始化</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"63349459527065700000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`mkdir art-api && cd art-api && npm init`, `63349459527065700000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> art-api <span class=\"token operator\">&amp;&amp;</span> <span class=\"token builtin class-name\">cd</span> art-api <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">npm</span> init</code></pre></div>\n<p>安装依赖（期望获取 ts 类型提示，请自行安装 @types）</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"75730408193434030000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`npm i koa @koa/router @koa/cors koa-body typescript ts-node cos-nodejs-sdk-v5 axios dotenv`, `75730408193434030000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> i koa @koa/router @koa/cors koa-body typescript ts-node cos-nodejs-sdk-v5 axios dotenv</code></pre></div>\n<p>配置 <code class=\"language-text\">tsconfig.json</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"73860995601825600000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\n  &quot;compilerOptions&quot;: {\n    &quot;target&quot;: &quot;es2018&quot;,\n    &quot;module&quot;: &quot;commonjs&quot;,\n    &quot;lib&quot;: [&quot;es2018&quot;, &quot;esnext.asynciterable&quot;],\n    &quot;experimentalDecorators&quot;: true,\n    &quot;emitDecoratorMetadata&quot;: true,\n    &quot;esModuleInterop&quot;: true\n  }\n}`, `73860995601825600000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"compilerOptions\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"target\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"es2018\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"module\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"commonjs\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"lib\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"es2018\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"esnext.asynciterable\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"experimentalDecorators\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"emitDecoratorMetadata\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"esModuleInterop\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>入口文件 <code class=\"language-text\">sls.js</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"24859371956167365000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`require(&quot;ts-node&quot;).register({ transpileOnly: true }); // 载入 ts 运行时环境，配置忽略类型错误\nmodule.exports = require(&quot;./app.ts&quot;); // 直接引入业务逻辑，下面我会和你一起实现`, `24859371956167365000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"ts-node\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">register</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> transpileOnly<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 载入 ts 运行时环境，配置忽略类型错误</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"./app.ts\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 直接引入业务逻辑，下面我会和你一起实现</span></code></pre></div>\n<p>补充两个实用知识点：</p>\n<p><strong>node -r</strong></p>\n<p>在入口文件中引入 <code class=\"language-text\">require(&quot;ts-node&quot;).register({ transpileOnly: true })</code> 实际等同于 <code class=\"language-text\">node -r ts-node/register/transpile-only</code></p>\n<p>所以 <code class=\"language-text\">node -r</code> 就是在执行之前载入一些特定模块，利用这个能力，能快速实现对一些功能的支持</p>\n<p>比如 <code class=\"language-text\">node -r esm main.js</code> 通过 esm 模块就能在无需 babel、webpack 的情况下快速 import 与 export 进行模块加载与导出</p>\n<p><strong>ts 加载路径</strong></p>\n<p>如果不希望用 <code class=\"language-text\">../../../../../</code> 来加载模块，那么</p>\n<ol>\n<li>在 tsconfig.json 中配置 <code class=\"language-text\">baseUrl: &quot;.&quot;</code></li>\n<li><code class=\"language-text\">ts-node -r tsconfig-paths/register main.ts</code> 或 <code class=\"language-text\">require(&quot;tsconfig-paths&quot;).register()</code></li>\n<li><code class=\"language-text\">import utils from &#39;src/utils&#39;</code> 即可愉快地从项目根路径加载模块</li>\n</ol>\n<p>下面来实现具体逻辑：</p>\n<p>app.ts </p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"34487642770911010000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`require(&quot;dotenv&quot;).config(); // 载入 .env 环境变量，可以将一些密钥配置在环境变量中，并通过 .gitignore 阻止提交\nimport Koa from &quot;koa&quot;;\nimport Router from &quot;@koa/router&quot;;\nimport koaBody from &quot;koa-body&quot;;\nimport cors from '@koa/cors'\nimport util from 'util'\nimport COS from 'cos-nodejs-sdk-v5'\nimport axios from 'axios'\n\nconst app = new Koa();\nconst router = new Router();\n\nvar cos = new COS({\n  SecretId: process.env.SecretId // 你的id,\n  SecretKey: process.env.SecretKey // 你的key,\n});\n\nconst cosInfo = {\n  Bucket: &quot;xart-oss-<你的appid>&quot;, // 部署oss后获取\n  Region: &quot;ap-guangzhou&quot;,\n}\n\nconst putObjectSync = util.promisify(cos.putObject.bind(cos));\nconst getBucketSync = util.promisify(cos.getBucket.bind(cos));\n\nrouter.get(&quot;/hello&quot;, async (ctx) => {\n  ctx.body = 'hello world!'\n})\n\nrouter.get(&quot;/api/images&quot;, async (ctx) => {\n  const files = await getBucketSync({\n    ...cosInfo,\n    Prefix: &quot;result&quot;,\n  });\n\n  const cosURL = \\`https://\\${cosInfo.Bucket}.cos.\\${cosInfo.Region}.myqcloud.com\\`;\n  ctx.body = files.Contents.map((it) => {\n    const [timestamp, size] = it.Key.split(&quot;.jpg&quot;)[0].split(&quot;__&quot;);\n    const [width, height] = size.split(&quot;_&quot;);\n    return {\n      url: \\`\\${cosURL}/\\${it.Key}\\`,\n      width,\n      height,\n      timestamp: Number(timestamp),\n      name: it.Key,\n    };\n  })\n    .filter(Boolean)\n    .sort((a, b) => b.timestamp - a.timestamp);\n});\n\nrouter.post(&quot;/api/images/upload&quot;, async (ctx) => {\n  const { imgBase64, style } = JSON.parse(ctx.request.body)\n  const buf = Buffer.from(imgBase64.replace(/^data:image\\/\\w+;base64,/, &quot;&quot;), 'base64')\n  // 调用预先提供tensorflow服务加工图片，后面替换成你自己的服务\n  const { data } = await axios.post('https://service-edtflvxk-1254074572.gz.apigw.tencentcs.com/release/', {\n    imgBase64: buf.toString('base64'),\n    style\n  })\n  if (data.success) {\n    const afterImg = await putObjectSync({\n      ...cosInfo,\n      Key: \\`result/\\${Date.now()}__400_200.jpg\\`,\n      Body: Buffer.from(data.data, 'base64'),\n    });\n    ctx.body = {\n      success: true,\n      data: 'https://' + afterImg.Location\n    }\n  }\n});\n\napp.use(cors());\napp.use(koaBody({\n  formLimit: &quot;10mb&quot;,\n  jsonLimit: '10mb',\n  textLimit: &quot;10mb&quot;\n}));\napp.use(router.routes()).use(router.allowedMethods());\n\nconst port = 8080;\napp.listen(port, () => {\n  console.log(&quot;listen in http://localhost:%s&quot;, port);\n});\n\nmodule.exports = app;`, `34487642770911010000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"dotenv\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">config</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 载入 .env 环境变量，可以将一些密钥配置在环境变量中，并通过 .gitignore 阻止提交</span>\n<span class=\"token keyword\">import</span> Koa <span class=\"token keyword\">from</span> <span class=\"token string\">\"koa\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> Router <span class=\"token keyword\">from</span> <span class=\"token string\">\"@koa/router\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> koaBody <span class=\"token keyword\">from</span> <span class=\"token string\">\"koa-body\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> cors <span class=\"token keyword\">from</span> <span class=\"token string\">'@koa/cors'</span>\n<span class=\"token keyword\">import</span> util <span class=\"token keyword\">from</span> <span class=\"token string\">'util'</span>\n<span class=\"token keyword\">import</span> <span class=\"token constant\">COS</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'cos-nodejs-sdk-v5'</span>\n<span class=\"token keyword\">import</span> axios <span class=\"token keyword\">from</span> <span class=\"token string\">'axios'</span>\n\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Koa</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> router <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Router</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">var</span> cos <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">COS</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  SecretId<span class=\"token punctuation\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span>SecretId <span class=\"token comment\">// 你的id,</span>\n  SecretKey<span class=\"token punctuation\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span>SecretKey <span class=\"token comment\">// 你的key,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> cosInfo <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  Bucket<span class=\"token punctuation\">:</span> <span class=\"token string\">\"xart-oss-&lt;你的appid>\"</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// 部署oss后获取</span>\n  Region<span class=\"token punctuation\">:</span> <span class=\"token string\">\"ap-guangzhou\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> putObjectSync <span class=\"token operator\">=</span> util<span class=\"token punctuation\">.</span><span class=\"token function\">promisify</span><span class=\"token punctuation\">(</span>cos<span class=\"token punctuation\">.</span><span class=\"token function\">putObject</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span>cos<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> getBucketSync <span class=\"token operator\">=</span> util<span class=\"token punctuation\">.</span><span class=\"token function\">promisify</span><span class=\"token punctuation\">(</span>cos<span class=\"token punctuation\">.</span><span class=\"token function\">getBucket</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span>cos<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\nrouter<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/hello\"</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">ctx</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  ctx<span class=\"token punctuation\">.</span>body <span class=\"token operator\">=</span> <span class=\"token string\">'hello world!'</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\nrouter<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/api/images\"</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">ctx</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> files <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getBucketSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>cosInfo<span class=\"token punctuation\">,</span>\n    Prefix<span class=\"token punctuation\">:</span> <span class=\"token string\">\"result\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> cosURL <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">https://</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>cosInfo<span class=\"token punctuation\">.</span>Bucket<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.cos.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>cosInfo<span class=\"token punctuation\">.</span>Region<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.myqcloud.com</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n  ctx<span class=\"token punctuation\">.</span>body <span class=\"token operator\">=</span> files<span class=\"token punctuation\">.</span>Contents<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">it</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>timestamp<span class=\"token punctuation\">,</span> size<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> it<span class=\"token punctuation\">.</span>Key<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">\".jpg\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"__\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>width<span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> size<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"_\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      url<span class=\"token punctuation\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>cosURL<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>it<span class=\"token punctuation\">.</span>Key<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      width<span class=\"token punctuation\">,</span>\n      height<span class=\"token punctuation\">,</span>\n      timestamp<span class=\"token punctuation\">:</span> <span class=\"token function\">Number</span><span class=\"token punctuation\">(</span>timestamp<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      name<span class=\"token punctuation\">:</span> it<span class=\"token punctuation\">.</span>Key<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> b<span class=\"token punctuation\">.</span>timestamp <span class=\"token operator\">-</span> a<span class=\"token punctuation\">.</span>timestamp<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\nrouter<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/api/images/upload\"</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">ctx</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> imgBase64<span class=\"token punctuation\">,</span> style <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">.</span>request<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> buf <span class=\"token operator\">=</span> Buffer<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>imgBase64<span class=\"token punctuation\">.</span><span class=\"token function\">replace</span><span class=\"token punctuation\">(</span><span class=\"token regex\">/^data:image\\/\\w+;base64,/</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'base64'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token comment\">// 调用预先提供tensorflow服务加工图片，后面替换成你自己的服务</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> axios<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://service-edtflvxk-1254074572.gz.apigw.tencentcs.com/release/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    imgBase64<span class=\"token punctuation\">:</span> buf<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'base64'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    style\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span>success<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> afterImg <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">putObjectSync</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token operator\">...</span>cosInfo<span class=\"token punctuation\">,</span>\n      Key<span class=\"token punctuation\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">result/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">__400_200.jpg</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      Body<span class=\"token punctuation\">:</span> Buffer<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">,</span> <span class=\"token string\">'base64'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    ctx<span class=\"token punctuation\">.</span>body <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n      success<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n      data<span class=\"token punctuation\">:</span> <span class=\"token string\">'https://'</span> <span class=\"token operator\">+</span> afterImg<span class=\"token punctuation\">.</span>Location\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token function\">cors</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token function\">koaBody</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  formLimit<span class=\"token punctuation\">:</span> <span class=\"token string\">\"10mb\"</span><span class=\"token punctuation\">,</span>\n  jsonLimit<span class=\"token punctuation\">:</span> <span class=\"token string\">'10mb'</span><span class=\"token punctuation\">,</span>\n  textLimit<span class=\"token punctuation\">:</span> <span class=\"token string\">\"10mb\"</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span>router<span class=\"token punctuation\">.</span><span class=\"token function\">routes</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span>router<span class=\"token punctuation\">.</span><span class=\"token function\">allowedMethods</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> port <span class=\"token operator\">=</span> <span class=\"token number\">8080</span><span class=\"token punctuation\">;</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">listen</span><span class=\"token punctuation\">(</span>port<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"listen in http://localhost:%s\"</span><span class=\"token punctuation\">,</span> port<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> app<span class=\"token punctuation\">;</span></code></pre></div>\n<p>在代码里可以看到，在图片上传采用了 base64 的形式。这里需要注意，通过 api 网关触发 scf 的时候，网关无法透传 binary，具体上传规则可以参阅官方文档：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651515005-v2-a47c5e265c7a5ab449a548ebd3825222_b.png\"></p>\n<p>再补充一个知识点：实际我们访问的是 api 网关，然后触发云函数，来获得请求返回结果，所以 debug 时需要关注全链路</p>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651534103-v2-d99e3200ff10dc46b0aecdd3ceb5aca7_b.png\"></p>\n<p>回归正题，接着配置环境变量 <code class=\"language-text\">.env</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"25137259507177378000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`NODE_ENV=development\n\n# 配置 oss 上传所需密钥，需要自行配置，配好了也别告诉我：）\n# 密钥查看地址：https://console.cloud.tencent.com/cam/capi\nSecretId=xxxx\nSecretKey=xxxx`, `25137259507177378000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token assign-left variable\">NODE_ENV</span><span class=\"token operator\">=</span>development\n\n<span class=\"token comment\"># 配置 oss 上传所需密钥，需要自行配置，配好了也别告诉我：）</span>\n<span class=\"token comment\"># 密钥查看地址：https://console.cloud.tencent.com/cam/capi</span>\n<span class=\"token assign-left variable\">SecretId</span><span class=\"token operator\">=</span>xxxx\n<span class=\"token assign-left variable\">SecretKey</span><span class=\"token operator\">=</span>xxxx</code></pre></div>\n<p>以上，server 部分就开发完成了，我们可以通过在本地执行 <code class=\"language-text\">node sls.js</code> 来验证一下，应该可以看到服务启动的提示了。</p>\n<blockquote>\n<p>listen in <a href=\"http://localhost:8080\">http://localhost:8080</a> </p>\n</blockquote>\n<p>来简单配置一下 <code class=\"language-text\">serverless.yml</code>，把服务部署到线上，后面再进一步使用 <code class=\"language-text\">layer</code> 进行优化</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"64652523503188510000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`component: koa # 这里填写对应的 component\napp: art\nname: art-api\nstage: dev\n\ninputs:\n  src:\n    src: ./\n    exclude:\n      - .env\n  functionName: \\${name}\n  region: ap-guangzhou\n  runtime: Nodejs10.15\n  functionConf:\n    timeout: 60 # 超时时间配置的稍微久一点\n    environment:\n      variables: # 配置环境变量，同时也可以直接在scf控制台配置\n        NODE_ENV: production\n  apigatewayConf:\n    enableCORS: true\n    protocols:\n      - https\n      - http\n    environment: release`, `64652523503188510000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> koa <span class=\"token comment\"># 这里填写对应的 component</span>\n<span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> art\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> art<span class=\"token punctuation\">-</span>api\n<span class=\"token key atrule\">stage</span><span class=\"token punctuation\">:</span> dev\n\n<span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span> ./\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env\n  <span class=\"token key atrule\">functionName</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>name<span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>guangzhou\n  <span class=\"token key atrule\">runtime</span><span class=\"token punctuation\">:</span> Nodejs10.15\n  <span class=\"token key atrule\">functionConf</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">timeout</span><span class=\"token punctuation\">:</span> <span class=\"token number\">60 </span><span class=\"token comment\"># 超时时间配置的稍微久一点</span>\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">variables</span><span class=\"token punctuation\">:</span> <span class=\"token comment\"># 配置环境变量，同时也可以直接在scf控制台配置</span>\n        <span class=\"token key atrule\">NODE_ENV</span><span class=\"token punctuation\">:</span> production\n  <span class=\"token key atrule\">apigatewayConf</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">enableCORS</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n    <span class=\"token key atrule\">protocols</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> https\n      <span class=\"token punctuation\">-</span> http\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span> release</code></pre></div>\n<p>之后执行部署命令 <code class=\"language-text\">sls deploy</code></p>\n<p>等待数十秒，应该会得到如下的输出结果（如果是第一次执行，需要平台方授权）</p>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651558871-v2-2384954cb77cbaf3abf1daaad7efe2c4_b.png\" alt=\"img\"></p>\n<p>其中 url 就是当前服务部署在线上的地址，我们可以试着访问一下看看，是否看到了预设的 hello world。</p>\n<p>到这里，server 基本上已经部署完成了。如果代码有改动，那就修改后再次执行 <code class=\"language-text\">sls deploy</code>。官方为代码小于 10M 的项目提供了在线编辑的能力。</p>\n<p>但是，随着项目复杂度的增加，deploy 上传会变慢。所以，让我们再优化一下。</p>\n<p>新建 <code class=\"language-text\">layer</code> 目录</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"32928539135180812000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`mkdir layer`, `32928539135180812000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> layer</code></pre></div>\n<p>在 <code class=\"language-text\">layer</code> 目录下添加 <code class=\"language-text\">serverless.yml</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"57693360865721280000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`component: layer\napp: art\nname: art-api-layer\nstage: dev\n\ninputs:\n  region: ap-guangzhou\n  name: \\${name}\n  src: ../node_modules # 将 node_modules 打包上传\n  runtimes:\n    - Nodejs10.15 # 注意配置为相同环境`, `57693360865721280000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> layer\n<span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> art\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> art<span class=\"token punctuation\">-</span>api<span class=\"token punctuation\">-</span>layer\n<span class=\"token key atrule\">stage</span><span class=\"token punctuation\">:</span> dev\n\n<span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>guangzhou\n  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>name<span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span> ../node_modules <span class=\"token comment\"># 将 node_modules 打包上传</span>\n  <span class=\"token key atrule\">runtimes</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> Nodejs10.15 <span class=\"token comment\"># 注意配置为相同环境</span></code></pre></div>\n<p>回到项目根目录，调整一下根目录的 <code class=\"language-text\">serverless.yml</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"40697797627495326000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`component: koa # 这里填写对应的 component\napp: art\nname: art-api\nstage: dev\n\ninputs:\n  src:\n    src: ./\n    exclude:\n      - .env\n      - node_modules/** # deploy 时排除 node_modules\n  functionName: \\${name}\n  region: ap-guangzhou\n  runtime: Nodejs10.15\n  functionConf:\n    timeout: 60 # 超时时间配置的稍微久一点\n    environment:\n      variables: # 配置环境变量，同时也可以直接在 scf 控制台配置\n        NODE_ENV: production\n  apigatewayConf:\n    enableCORS: true\n    protocols:\n      - https\n      - http\n    environment: release\n  layers:\n    - name: \\${output:\\${stage}:\\${app}:\\${name}-layer.name} # 配置对应的 layer\n      version: \\${output:\\${stage}:\\${app}:\\${name}-layer.version} # 配置对应的 layer 版本`, `40697797627495326000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> koa <span class=\"token comment\"># 这里填写对应的 component</span>\n<span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> art\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> art<span class=\"token punctuation\">-</span>api\n<span class=\"token key atrule\">stage</span><span class=\"token punctuation\">:</span> dev\n\n<span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span> ./\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env\n      <span class=\"token punctuation\">-</span> node_modules/** <span class=\"token comment\"># deploy 时排除 node_modules</span>\n  <span class=\"token key atrule\">functionName</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>name<span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>guangzhou\n  <span class=\"token key atrule\">runtime</span><span class=\"token punctuation\">:</span> Nodejs10.15\n  <span class=\"token key atrule\">functionConf</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">timeout</span><span class=\"token punctuation\">:</span> <span class=\"token number\">60 </span><span class=\"token comment\"># 超时时间配置的稍微久一点</span>\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">variables</span><span class=\"token punctuation\">:</span> <span class=\"token comment\"># 配置环境变量，同时也可以直接在 scf 控制台配置</span>\n        <span class=\"token key atrule\">NODE_ENV</span><span class=\"token punctuation\">:</span> production\n  <span class=\"token key atrule\">apigatewayConf</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">enableCORS</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n    <span class=\"token key atrule\">protocols</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> https\n      <span class=\"token punctuation\">-</span> http\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span> release\n  <span class=\"token key atrule\">layers</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>output<span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>stage<span class=\"token punctuation\">}</span><span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>app<span class=\"token punctuation\">}</span><span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>name<span class=\"token punctuation\">}</span><span class=\"token punctuation\">-</span>layer.name<span class=\"token punctuation\">}</span> <span class=\"token comment\"># 配置对应的 layer</span>\n      <span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>output<span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>stage<span class=\"token punctuation\">}</span><span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>app<span class=\"token punctuation\">}</span><span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>name<span class=\"token punctuation\">}</span><span class=\"token punctuation\">-</span>layer.version<span class=\"token punctuation\">}</span> <span class=\"token comment\"># 配置对应的 layer 版本</span></code></pre></div>\n<p>接着执行命令 <code class=\"language-text\">sls deploy --target=./layer</code> 部署 <code class=\"language-text\">layer</code>，然后这次部署看看速度应该已经在 10s 左右了</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"7269069662897887000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`sls deploy`, `7269069662897887000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">sls deploy</code></pre></div>\n<p>关于 layer 和云函数，补充两个知识点：</p>\n<p><strong>layer 的加载与访问</strong></p>\n<p>layer 会在函数运行时，将内容解压到 <code class=\"language-text\">/opt</code> 目录下，如果存在多个 layer，那么会按时间循序进行解压。如果需要访问 layer 内的文件，可以直接通过 <code class=\"language-text\">/opt/xxx</code> 访问。如果是访问 <code class=\"language-text\">node_module</code> 则可以直接 <code class=\"language-text\">import</code>，因为 scf 的 <code class=\"language-text\">NODE_PATH</code> 环境变量默认已包含 <code class=\"language-text\">/opt/node_modules</code> 路径。</p>\n<p><strong>配额</strong></p>\n<p>云函数 scf 针对每个用户帐号，均有一定的配额限制：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651593275-v2-35a92707d47d21e22f162774dbfa9190_b.png\" alt=\"img\"></p>\n<p>其中需要重点关注的就是单个函数代码体积 500mb 的上限。在实际操作中，云函数虽然提供了 500mb。但也存在着一个 deploy 解压上限。</p>\n<p>关于绕过配额问题：</p>\n<ul>\n<li>如果超的不多，那么使用 <code class=\"language-text\">npm install --production</code> 就能解决问题</li>\n<li>如果超的太多，那就通过挂载 cfs 文件系统来进行规避，我会在下面部署 tensorflow 算法模型服务章节里面，展开聊聊如何把 800mb tensorflow 的包 + 模型部署到 SCF 上</li>\n</ul>\n<h2 id=\"实现前端-ssr-服务\"><a href=\"#%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF-ssr-%E6%9C%8D%E5%8A%A1\" aria-label=\"实现前端 ssr 服务 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>实现前端 SSR 服务</h2>\n<p>下面将使用 next.js 来构建一个前端 SSR 服务。</p>\n<p>新建目录并初始化项目：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"16689580826410922000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`mkdir art-front && cd art-front && npm init`, `16689580826410922000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> art-front <span class=\"token operator\">&amp;&amp;</span> <span class=\"token builtin class-name\">cd</span> art-front <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">npm</span> init</code></pre></div>\n<p>安装依赖：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"44762914058926290000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`npm install next react react-dom typescript @types/node swr antd @ant-design/icons dayjs`, `44762914058926290000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> next react react-dom typescript @types/node swr antd @ant-design/icons dayjs</code></pre></div>\n<p>增加 ts 支持（next.js 跑起来会自动配置）：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"72204225586875710000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`touch tsconfig.json`, `72204225586875710000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">touch</span> tsconfig.json</code></pre></div>\n<p>打开 package.json 文件并添加 scripts 配置段：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">&quot;scripts&quot;: {\n  &quot;dev&quot;: &quot;next&quot;,\n  &quot;build&quot;: &quot;next build&quot;,\n  &quot;start&quot;: &quot;next start&quot;\n}</code></pre></div>\n<p>编写前端业务逻辑（文中仅展示主要逻辑，源码在 GitHub 获取）</p>\n<p>pages/_app.tsx</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"78106504952225070000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import React from &quot;react&quot;;\nimport &quot;antd/dist/antd.css&quot;;\nimport { SWRConfig } from &quot;swr&quot;;\n\nexport default function MyApp({ Component, pageProps }) {\n  return (\n    <SWRConfig\n      value={{\n        refreshInterval: 2000,\n        fetcher: (...args) => fetch(args[0], args[1]).then((res) => res.json()),\n      }}\n    >\n      <Component {...pageProps} />\n    </SWRConfig>\n  );\n}`, `78106504952225070000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React <span class=\"token keyword\">from</span> <span class=\"token string\">\"react\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">\"antd/dist/antd.css\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> SWRConfig <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"swr\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\">MyApp</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> Component<span class=\"token punctuation\">,</span> pageProps <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token operator\">&lt;</span>SWRConfig\n      value<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span>\n        refreshInterval<span class=\"token punctuation\">:</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function-variable function\">fetcher</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>args<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> args<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n    <span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span>Component <span class=\"token punctuation\">{</span><span class=\"token operator\">...</span>pageProps<span class=\"token punctuation\">}</span> <span class=\"token operator\">/</span><span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>SWRConfig<span class=\"token operator\">></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>pages/index.tsx  <a href=\"https://github.com/jiangqizheng/art/blob/master/art-front/pages/index.tsx\">完整代码</a></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"21008091028635013000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import React from &quot;react&quot;;\nimport { Card, Upload, message, Radio, Spin, Divider } from &quot;antd&quot;;\nimport { InboxOutlined } from &quot;@ant-design/icons&quot;;\nimport dayjs from &quot;dayjs&quot;;\nimport useSWR from &quot;swr&quot;;\n\nlet origin = 'http://localhost:8080'\nif (process.env.NODE_ENV === 'production') {\n  // 使用你自己的部署的art-api服务地址\n  origin = 'https://service-5yyo7qco-1254074572.gz.apigw.tencentcs.com/release' \n}\n\n// 略...\nexport default function Index() {\n  const { data } = useSWR(\\`\\${origin}/api/images\\`);\n\n  const [img, setImg] = React.useState(&quot;&quot;);\n  const [loading, setLoading] = React.useState(false);\n\n  const uploadImg = React.useCallback((file, style) => {\n    const reader = new FileReader();\n    reader.readAsDataURL(file);\n    reader.onload = async () => {\n      const res = await fetch(\n        \\`\\${origin}/api/images/upload\\`, {\n        method: 'POST',\n        body: JSON.stringify({\n          imgBase64: reader.result,\n          style\n        }),\n        mode: 'cors'\n      }\n      ).then((res) => res.json());\n\n      if (res.success) {\n        setImg(res.data);\n      } else {\n        message.error(res.message);\n      }\n      setLoading(false);\n    }\n  }, []);\n\n  const [artStyle, setStyle] = React.useState(STYLE_MODE.cube);\n\n  return (\n        <Dragger\n          style={{ padding: 24 }}\n          {...{\n            name: &quot;art_img&quot;,\n            showUploadList: false,\n            action: \\`\\${origin}/api/upload\\`,\n            onChange: (info) => {\n              const { status } = info.file;\n              if (status !== &quot;uploading&quot;) {\n                console.log(info.file, info.fileList);\n              }\n              if (status === &quot;done&quot;) {\n                setImg(info.file.response);\n                message.success(\\`\\${info.file.name} 上传成功\\`);\n                setLoading(false);\n              } else if (status === &quot;error&quot;) {\n                message.error(\\`\\${info.file.name} 上传失败\\`);\n                setLoading(false);\n              }\n            },\n            beforeUpload: (file) => {\n              if (\n                ![&quot;image/png&quot;, &quot;image/jpg&quot;, &quot;image/jpeg&quot;].includes(file.type)\n              ) {\n                message.error(&quot;图片格式必须是 png、jpg、jpeg&quot;);\n                return false;\n              }\n              const isLt10M = file.size / 1024 / 1024 < 10;\n              if (!isLt10M) {\n                message.error(&quot;文件大小超过10M&quot;);\n                return false;\n              }\n              setLoading(true);\n\n              uploadImg(file, artStyle);\n              return false;\n            },\n          }}\n      // 略...`, `21008091028635013000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> React <span class=\"token keyword\">from</span> <span class=\"token string\">\"react\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> Card<span class=\"token punctuation\">,</span> Upload<span class=\"token punctuation\">,</span> message<span class=\"token punctuation\">,</span> Radio<span class=\"token punctuation\">,</span> Spin<span class=\"token punctuation\">,</span> Divider <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"antd\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> InboxOutlined <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"@ant-design/icons\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> dayjs <span class=\"token keyword\">from</span> <span class=\"token string\">\"dayjs\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> useSWR <span class=\"token keyword\">from</span> <span class=\"token string\">\"swr\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">let</span> origin <span class=\"token operator\">=</span> <span class=\"token string\">'http://localhost:8080'</span>\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">NODE_ENV</span> <span class=\"token operator\">===</span> <span class=\"token string\">'production'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 使用你自己的部署的art-api服务地址</span>\n  origin <span class=\"token operator\">=</span> <span class=\"token string\">'https://service-5yyo7qco-1254074572.gz.apigw.tencentcs.com/release'</span> \n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// 略...</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\">Index</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useSWR</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>origin<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/api/images</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>img<span class=\"token punctuation\">,</span> setImg<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>loading<span class=\"token punctuation\">,</span> setLoading<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> uploadImg <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">useCallback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">file<span class=\"token punctuation\">,</span> style</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> reader <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FileReader</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    reader<span class=\"token punctuation\">.</span><span class=\"token function\">readAsDataURL</span><span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    reader<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">onload</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>\n        <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>origin<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/api/images/upload</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        method<span class=\"token punctuation\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n        body<span class=\"token punctuation\">:</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n          imgBase64<span class=\"token punctuation\">:</span> reader<span class=\"token punctuation\">.</span>result<span class=\"token punctuation\">,</span>\n          style\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        mode<span class=\"token punctuation\">:</span> <span class=\"token string\">'cors'</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>success<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setImg</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n        message<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>message<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n      <span class=\"token function\">setLoading</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>artStyle<span class=\"token punctuation\">,</span> setStyle<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token constant\">STYLE_MODE</span><span class=\"token punctuation\">.</span>cube<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n        <span class=\"token operator\">&lt;</span>Dragger\n          style<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> padding<span class=\"token punctuation\">:</span> <span class=\"token number\">24</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">{</span><span class=\"token operator\">...</span><span class=\"token punctuation\">{</span>\n            name<span class=\"token punctuation\">:</span> <span class=\"token string\">\"art_img\"</span><span class=\"token punctuation\">,</span>\n            showUploadList<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n            action<span class=\"token punctuation\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>origin<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/api/upload</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n            <span class=\"token function-variable function\">onChange</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">info</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> status <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> info<span class=\"token punctuation\">.</span>file<span class=\"token punctuation\">;</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">!==</span> <span class=\"token string\">\"uploading\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>info<span class=\"token punctuation\">.</span>file<span class=\"token punctuation\">,</span> info<span class=\"token punctuation\">.</span>fileList<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">===</span> <span class=\"token string\">\"done\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setImg</span><span class=\"token punctuation\">(</span>info<span class=\"token punctuation\">.</span>file<span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                message<span class=\"token punctuation\">.</span><span class=\"token function\">success</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>info<span class=\"token punctuation\">.</span>file<span class=\"token punctuation\">.</span>name<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> 上传成功</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token function\">setLoading</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>status <span class=\"token operator\">===</span> <span class=\"token string\">\"error\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                message<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>info<span class=\"token punctuation\">.</span>file<span class=\"token punctuation\">.</span>name<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> 上传失败</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token function\">setLoading</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function-variable function\">beforeUpload</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">file</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>\n                <span class=\"token operator\">!</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"image/png\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"image/jpg\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"image/jpeg\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">includes</span><span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">.</span>type<span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                message<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"图片格式必须是 png、jpg、jpeg\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n              <span class=\"token keyword\">const</span> isLt10M <span class=\"token operator\">=</span> file<span class=\"token punctuation\">.</span>size <span class=\"token operator\">/</span> <span class=\"token number\">1024</span> <span class=\"token operator\">/</span> <span class=\"token number\">1024</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span>\n              <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>isLt10M<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                message<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"文件大小超过10M\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n              <span class=\"token punctuation\">}</span>\n              <span class=\"token function\">setLoading</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n              <span class=\"token function\">uploadImg</span><span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">,</span> artStyle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n              <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n      <span class=\"token comment\">// 略...</span></code></pre></div>\n<p>使用 <code class=\"language-text\">npm run dev</code> 把前端跑起来看看，看到以下提示就是成功了</p>\n<blockquote>\n<p>ready - started server on <a href=\"http://localhost:3000\">http://localhost:3000</a> </p>\n</blockquote>\n<p>接着配置 <code class=\"language-text\">serverless.yml</code>（如果有需要可以参考前文，使用 layer 优化部署体验）</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"87967988390170700000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`component: nextjs\napp: art\nname: art-front\nstage: dev\n\ninputs:\n  src:\n    dist: ./\n    hook: npm run build\n    exclude:\n      - .env\n  region: ap-guangzhou\n  functionName: \\${name}\n  runtime: Nodejs12.16\n  staticConf:\n    cosConf:\n      bucket: art-front # 将前端静态资源部署到oss，减少scf的调用频次\n  apigatewayConf:\n    enableCORS: true\n    protocols:\n      - https\n      - http\n    environment: release\n    # customDomains: # 如果需要，可以自己配置自定义域名\n    #   - domain: xxxxx \n    #     certificateId: xxxxx # 证书 ID\n    #     # 这里将 API 网关的 release 环境映射到根路径\n    #     isDefaultMapping: false\n    #     pathMappingSet:\n    #       - path: /\n    #         environment: release\n    #     protocols:\n    #       - https\n  functionConf:\n    timeout: 60\n    memorySize: 128\n    environment:\n      variables:\n        apiUrl: \\${output:\\${stage}:\\${app}:art-api.apigw.url} # 此处可以将api通过环境变量注入`, `87967988390170700000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> nextjs\n<span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> art\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> art<span class=\"token punctuation\">-</span>front\n<span class=\"token key atrule\">stage</span><span class=\"token punctuation\">:</span> dev\n\n<span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">dist</span><span class=\"token punctuation\">:</span> ./\n    <span class=\"token key atrule\">hook</span><span class=\"token punctuation\">:</span> npm run build\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env\n  <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>guangzhou\n  <span class=\"token key atrule\">functionName</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>name<span class=\"token punctuation\">}</span>\n  <span class=\"token key atrule\">runtime</span><span class=\"token punctuation\">:</span> Nodejs12.16\n  <span class=\"token key atrule\">staticConf</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">cosConf</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">bucket</span><span class=\"token punctuation\">:</span> art<span class=\"token punctuation\">-</span>front <span class=\"token comment\"># 将前端静态资源部署到oss，减少scf的调用频次</span>\n  <span class=\"token key atrule\">apigatewayConf</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">enableCORS</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n    <span class=\"token key atrule\">protocols</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> https\n      <span class=\"token punctuation\">-</span> http\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span> release\n    <span class=\"token comment\"># customDomains: # 如果需要，可以自己配置自定义域名</span>\n    <span class=\"token comment\">#   - domain: xxxxx </span>\n    <span class=\"token comment\">#     certificateId: xxxxx # 证书 ID</span>\n    <span class=\"token comment\">#     # 这里将 API 网关的 release 环境映射到根路径</span>\n    <span class=\"token comment\">#     isDefaultMapping: false</span>\n    <span class=\"token comment\">#     pathMappingSet:</span>\n    <span class=\"token comment\">#       - path: /</span>\n    <span class=\"token comment\">#         environment: release</span>\n    <span class=\"token comment\">#     protocols:</span>\n    <span class=\"token comment\">#       - https</span>\n  <span class=\"token key atrule\">functionConf</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">timeout</span><span class=\"token punctuation\">:</span> <span class=\"token number\">60</span>\n    <span class=\"token key atrule\">memorySize</span><span class=\"token punctuation\">:</span> <span class=\"token number\">128</span>\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">variables</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">apiUrl</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>output<span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>stage<span class=\"token punctuation\">}</span><span class=\"token punctuation\">:</span>$<span class=\"token punctuation\">{</span>app<span class=\"token punctuation\">}</span><span class=\"token punctuation\">:</span>art<span class=\"token punctuation\">-</span>api.apigw.url<span class=\"token punctuation\">}</span> <span class=\"token comment\"># 此处可以将api通过环境变量注入</span></code></pre></div>\n<p>由于我们额外配置了 oss，所以需要额外配置一下 <code class=\"language-text\">next.config.js</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"47414677839079980000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const isProd = process.env.NODE_ENV === &quot;production&quot;;\n\nconst STATIC_URL =\n  &quot;https://art-front-<你的appid>.cos.ap-guangzhou.myqcloud.com/&quot;;\n\nmodule.exports = {\n  assetPrefix: isProd ? STATIC_URL : &quot;&quot;,\n};`, `47414677839079980000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> isProd <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">NODE_ENV</span> <span class=\"token operator\">===</span> <span class=\"token string\">\"production\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token constant\">STATIC_URL</span> <span class=\"token operator\">=</span>\n  <span class=\"token string\">\"https://art-front-&lt;你的appid>.cos.ap-guangzhou.myqcloud.com/\"</span><span class=\"token punctuation\">;</span>\n\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  assetPrefix<span class=\"token punctuation\">:</span> isProd <span class=\"token operator\">?</span> <span class=\"token constant\">STATIC_URL</span> <span class=\"token punctuation\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<h2 id=\"提供-tensorflow-2x-算法模型服务\"><a href=\"#%E6%8F%90%E4%BE%9B-tensorflow-2x-%E7%AE%97%E6%B3%95%E6%A8%A1%E5%9E%8B%E6%9C%8D%E5%8A%A1\" aria-label=\"提供 tensorflow 2x 算法模型服务 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>提供 Tensorflow 2.x 算法模型服务</h2>\n<p>在上面的例子中，我们使用的 Tensorflow，暂时还是调用我预先提供的接口。</p>\n<p>接着让我们会把它替换成我们自己的服务。</p>\n<p><strong>基础信息</strong></p>\n<ul>\n<li>tensoflow2.3</li>\n<li><a href=\"https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2\">model</a></li>\n</ul>\n<p>scf 在 python 环境下，默认提供了 tensorflow1.9 依赖包，使用 python 可以用较低的成本直接上手。</p>\n<p><strong>问题所在</strong></p>\n<p>但如果你想使用 2.x 版本，或不熟悉 python，想用 node 来跑 tensorflow，那么就会遇到代码包大小的限制的问题。</p>\n<ul>\n<li>Python 中 Tensorflow 2.3 包体积 800mb 左右 </li>\n<li>node 中 tfjs-node2.3 安装后，同样会超过 400mb（tfjs core 版本，非常小，不过速度太慢） </li>\n</ul>\n<p><strong>怎么解决</strong> —— 文件存储服务！</p>\n<p>先看看 <a href=\"https://cloud.tencent.com/document/product/583/46199\">CFS 文档</a>的介绍</p>\n<p><img src=\"https://img.serverlesscloud.cn/202099/1599651653678-v2-1ddde91871f80f65cf2bc3e44938f489_b.png\" alt=\"img\"></p>\n<p>挂载后，就可以正常使用了，腾讯云提供了一个简单例子。</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"36805561533587140000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`var fs = requiret('fs');\nexports.main_handler = async (event, context) => {\n  await fs.promises.writeFile('/mnt/myfolder/filel.txt', JSON.stringify(event)); \n  return event;\n};`, `36805561533587140000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">var</span> fs <span class=\"token operator\">=</span> <span class=\"token function\">requiret</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fs'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nexports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">main_handler</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">event<span class=\"token punctuation\">,</span> context</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">await</span> fs<span class=\"token punctuation\">.</span>promises<span class=\"token punctuation\">.</span><span class=\"token function\">writeFile</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/mnt/myfolder/filel.txt'</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n  <span class=\"token keyword\">return</span> event<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>既然能正常读写，那么就能够正常的载入 npm 包，可以看到我直接加载了 <code class=\"language-text\">/mnt</code> 目录下的包，同时 model 也放在 <code class=\"language-text\">/mnt</code> 下</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"21914615324599310000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`  tf = require(&quot;/mnt/nodelib/node_modules/@tensorflow/tfjs-node&quot;);\n  jpeg = require(&quot;/mnt/nodelib/node_modules/jpeg-js&quot;);\n  images = require(&quot;/mnt/nodelib/node_modules/images&quot;);\n  loadModel = async () => tf.node.loadSavedModel(&quot;/mnt/model&quot;);`, `21914615324599310000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">  tf <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/nodelib/node_modules/@tensorflow/tfjs-node\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  jpeg <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/nodelib/node_modules/jpeg-js\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  images <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/nodelib/node_modules/images\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function-variable function\">loadModel</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> tf<span class=\"token punctuation\">.</span>node<span class=\"token punctuation\">.</span><span class=\"token function\">loadSavedModel</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/model\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>如果你使用 Python，那么可能会遇到一个问题，那就是 scf 默认环境下提供了 tensorflow 1.9 的依赖包，所以需要使用 insert，提高 <code class=\"language-text\">/mnt</code> 目录下包的优先级</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"29365890373021730000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`sys.path.insert(0, &quot;./mnt/xxx&quot;)`, `29365890373021730000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\">sys<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">.</span>insert<span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"./mnt/xxx\"</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>上面提供了解决方案，那么具体开发中可能会感觉很麻烦，因为 csf 必须和 scf 配置在同一个子网内，无法挂载到本地进行操作。</p>\n<p>所以，在实际部署过程中，可以在对应网络下，购置一台按需计费的 ecs 云服务器实例。然后将硬盘挂载后，直接进行操作，最后在云函数成功部署后，销毁实例：）</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"45085693720251130000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`sudo yum install nfs-utils\nmkdir <待挂载目标目录>\nsudo mount -t nfs -o vers=4.0，noresvport <挂载点IP>:/ <待挂载目录>`, `45085693720251130000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">sudo</span> yum <span class=\"token function\">install</span> nfs-utils\n<span class=\"token function\">mkdir</span> <span class=\"token operator\">&lt;</span>待挂载目标目录<span class=\"token operator\">></span>\n<span class=\"token function\">sudo</span> <span class=\"token function\">mount</span> -t nfs -o <span class=\"token assign-left variable\">vers</span><span class=\"token operator\">=</span><span class=\"token number\">4.0</span>，noresvport <span class=\"token operator\">&lt;</span>挂载点IP<span class=\"token operator\">></span>:/ <span class=\"token operator\">&lt;</span>待挂载目录<span class=\"token operator\">></span></code></pre></div>\n<p>具体业务代码如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"85346656013542640000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const fs = require(&quot;fs&quot;);\nlet tf, jpeg, loadModel, images;\n\nif (process.env.NODE_ENV !== &quot;production&quot;) {\n  tf = require(&quot;@tensorflow/tfjs-node&quot;);\n  jpeg = require(&quot;jpeg-js&quot;);\n  images = require(&quot;images&quot;);\n  loadModel = async () => tf.node.loadSavedModel(&quot;./model&quot;);\n} else {\n  tf = require(&quot;/mnt/nodelib/node_modules/@tensorflow/tfjs-node&quot;);\n  jpeg = require(&quot;/mnt/nodelib/node_modules/jpeg-js&quot;);\n  images = require(&quot;/mnt/nodelib/node_modules/images&quot;);\n  loadModel = async () => tf.node.loadSavedModel(&quot;/mnt/model&quot;);\n}\n\nexports.main_handler = async (event) => {\n  const { imgBase64, style } = JSON.parse(event.body)\n  if (!imgBase64 || !style) {\n    return { success: false, message: &quot;需要提供完整的参数imgBase64、style&quot; };\n  }\n  time = Date.now();\n  console.log(&quot;解析图片--&quot;);\n  const styleImg = tf.node.decodeJpeg(fs.readFileSync(\\`./imgs/style_\\${style}.jpeg\\`));\n  const contentImg = tf.node.decodeJpeg(\n    images(Buffer.from(imgBase64, 'base64')).size(400).encode(&quot;jpg&quot;, { operation: 50 }) // 压缩图片尺寸\n  );\n  const a = styleImg.toFloat().div(tf.scalar(255)).expandDims();\n  const b = contentImg.toFloat().div(tf.scalar(255)).expandDims();\n  console.log(&quot;--解析图片 %s ms&quot;, Date.now() - time);\n\n\n  time = Date.now();\n  console.log(&quot;载入模型--&quot;);\n  const model = await loadModel();\n  console.log(&quot;--载入模型 %s ms&quot;, Date.now() - time);\n\n\n  time = Date.now();\n  console.log(&quot;执行模型--&quot;);\n  const stylized = tf.tidy(() => {\n    const x = model.predict([b, a])[0];\n    return x.squeeze();\n  });\n  console.log(&quot;--执行模型 %s ms&quot;, Date.now() - time);\n\n  time = Date.now();\n\n  const imgData = await tf.browser.toPixels(stylized);\n  var rawImageData = {\n    data: Buffer.from(imgData),\n    width: stylized.shape[1],\n    height: stylized.shape[0],\n  };\n\n  const result = images(jpeg.encode(rawImageData, 50).data)\n    .draw(\n      images(&quot;./imgs/logo.png&quot;),\n      Math.random() * rawImageData.width * 0.9,\n      Math.random() * rawImageData.height * 0.9\n    )\n    .encode(&quot;jpg&quot;, { operation: 50 });\n\n  return { success: true, data: result.toString('base64') };\n};`, `85346656013542640000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> fs <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"fs\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">let</span> tf<span class=\"token punctuation\">,</span> jpeg<span class=\"token punctuation\">,</span> loadModel<span class=\"token punctuation\">,</span> images<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">NODE_ENV</span> <span class=\"token operator\">!==</span> <span class=\"token string\">\"production\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  tf <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"@tensorflow/tfjs-node\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  jpeg <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"jpeg-js\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  images <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"images\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function-variable function\">loadModel</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> tf<span class=\"token punctuation\">.</span>node<span class=\"token punctuation\">.</span><span class=\"token function\">loadSavedModel</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"./model\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n  tf <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/nodelib/node_modules/@tensorflow/tfjs-node\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  jpeg <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/nodelib/node_modules/jpeg-js\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  images <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/nodelib/node_modules/images\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function-variable function\">loadModel</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> tf<span class=\"token punctuation\">.</span>node<span class=\"token punctuation\">.</span><span class=\"token function\">loadSavedModel</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/mnt/model\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\nexports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">main_handler</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> imgBase64<span class=\"token punctuation\">,</span> style <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>imgBase64 <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>style<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span> success<span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> message<span class=\"token punctuation\">:</span> <span class=\"token string\">\"需要提供完整的参数imgBase64、style\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  time <span class=\"token operator\">=</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"解析图片--\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> styleImg <span class=\"token operator\">=</span> tf<span class=\"token punctuation\">.</span>node<span class=\"token punctuation\">.</span><span class=\"token function\">decodeJpeg</span><span class=\"token punctuation\">(</span>fs<span class=\"token punctuation\">.</span><span class=\"token function\">readFileSync</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">./imgs/style_</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>style<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.jpeg</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> contentImg <span class=\"token operator\">=</span> tf<span class=\"token punctuation\">.</span>node<span class=\"token punctuation\">.</span><span class=\"token function\">decodeJpeg</span><span class=\"token punctuation\">(</span>\n    <span class=\"token function\">images</span><span class=\"token punctuation\">(</span>Buffer<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>imgBase64<span class=\"token punctuation\">,</span> <span class=\"token string\">'base64'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">size</span><span class=\"token punctuation\">(</span><span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"jpg\"</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> operation<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 压缩图片尺寸</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> a <span class=\"token operator\">=</span> styleImg<span class=\"token punctuation\">.</span><span class=\"token function\">toFloat</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">div</span><span class=\"token punctuation\">(</span>tf<span class=\"token punctuation\">.</span><span class=\"token function\">scalar</span><span class=\"token punctuation\">(</span><span class=\"token number\">255</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">expandDims</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> b <span class=\"token operator\">=</span> contentImg<span class=\"token punctuation\">.</span><span class=\"token function\">toFloat</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">div</span><span class=\"token punctuation\">(</span>tf<span class=\"token punctuation\">.</span><span class=\"token function\">scalar</span><span class=\"token punctuation\">(</span><span class=\"token number\">255</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">expandDims</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"--解析图片 %s ms\"</span><span class=\"token punctuation\">,</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> time<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n  time <span class=\"token operator\">=</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"载入模型--\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> model <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">loadModel</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"--载入模型 %s ms\"</span><span class=\"token punctuation\">,</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> time<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n  time <span class=\"token operator\">=</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"执行模型--\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> stylized <span class=\"token operator\">=</span> tf<span class=\"token punctuation\">.</span><span class=\"token function\">tidy</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> x <span class=\"token operator\">=</span> model<span class=\"token punctuation\">.</span><span class=\"token function\">predict</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>b<span class=\"token punctuation\">,</span> a<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> x<span class=\"token punctuation\">.</span><span class=\"token function\">squeeze</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"--执行模型 %s ms\"</span><span class=\"token punctuation\">,</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> time<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  time <span class=\"token operator\">=</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> imgData <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> tf<span class=\"token punctuation\">.</span>browser<span class=\"token punctuation\">.</span><span class=\"token function\">toPixels</span><span class=\"token punctuation\">(</span>stylized<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> rawImageData <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    data<span class=\"token punctuation\">:</span> Buffer<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>imgData<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    width<span class=\"token punctuation\">:</span> stylized<span class=\"token punctuation\">.</span>shape<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    height<span class=\"token punctuation\">:</span> stylized<span class=\"token punctuation\">.</span>shape<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token function\">images</span><span class=\"token punctuation\">(</span>jpeg<span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span>rawImageData<span class=\"token punctuation\">,</span> <span class=\"token number\">50</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">draw</span><span class=\"token punctuation\">(</span>\n      <span class=\"token function\">images</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"./imgs/logo.png\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> rawImageData<span class=\"token punctuation\">.</span>width <span class=\"token operator\">*</span> <span class=\"token number\">0.9</span><span class=\"token punctuation\">,</span>\n      Math<span class=\"token punctuation\">.</span><span class=\"token function\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> rawImageData<span class=\"token punctuation\">.</span>height <span class=\"token operator\">*</span> <span class=\"token number\">0.9</span>\n    <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">encode</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"jpg\"</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> operation<span class=\"token punctuation\">:</span> <span class=\"token number\">50</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span> success<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> data<span class=\"token punctuation\">:</span> result<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'base64'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<h2 id=\"最后\"><a href=\"#%E6%9C%80%E5%90%8E\" aria-label=\"最后 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>最后</h2>\n<p>感谢阅读，以上代码均经过实测，如果发现异常，那就再看一遍：）</p>\n<p>有其他问题或想法，可以移步<a href=\"https://zhuanlan.zhihu.com/p/218803108\">原文链接</a>讨论。</p>\n<blockquote>\n<p>源码：<a href=\"https://github.com/jiangqizheng/art\">jiangqizheng/art</a>，欢迎 star。</p>\n</blockquote>\n<p>欢迎访问：<a href=\"https://serverlesscloud.cn/\">Serverless 中文网</a>，您可以在 <a href=\"https://serverlesscloud.cn/best-practice\">最佳实践</a> 里体验更多关于 Serverless 应用的开发！</p>","tableOfContents":"<ul>\n<li><a href=\"/blog/2020-09-09-jiangqizheng-art/#%E9%9C%80%E6%B1%82%E4%B8%8E%E6%9E%B6%E6%9E%84\">需求与架构</a></li>\n<li><a href=\"/blog/2020-09-09-jiangqizheng-art/#%E7%94%A8%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8%E6%8F%90%E4%BE%9B%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1\">用对象存储提供存储服务</a></li>\n<li><a href=\"/blog/2020-09-09-jiangqizheng-art/#%E5%AE%9E%E7%8E%B0%E5%90%8E%E7%AB%AF%E6%9C%8D%E5%8A%A1\">实现后端服务</a></li>\n<li><a href=\"/blog/2020-09-09-jiangqizheng-art/#%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF-ssr-%E6%9C%8D%E5%8A%A1\">实现前端 SSR 服务</a></li>\n<li><a href=\"/blog/2020-09-09-jiangqizheng-art/#%E6%8F%90%E4%BE%9B-tensorflow-2x-%E7%AE%97%E6%B3%95%E6%A8%A1%E5%9E%8B%E6%9C%8D%E5%8A%A1\">提供 Tensorflow 2.x 算法模型服务</a></li>\n<li><a href=\"/blog/2020-09-09-jiangqizheng-art/#%E6%9C%80%E5%90%8E\">最后</a></li>\n</ul>"},"previousBlog":{"id":"d1aff9e6-c8bf-5f1c-bcba-eb69158b1825","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/202097/1599470747622-1599469754973-%E7%AC%AC%E5%8D%81%E6%9C%9F.jpg","authors":["Serverless 社区"],"categories":["meetup"],"date":"2020-09-11T00:00:00.000Z","title":"腾讯云 Serverless 网站托管和备案一站式服务体验 | 在线分享第十期","description":"腾讯云高级产品经理庞志伟与 CODING DevOps 高级开发工程师江裕诚为您分享！","authorslink":null,"translators":null,"translatorslink":null,"tags":["Serverless","Meetup"],"keywords":"Serverless CFS","outdated":null},"wordCount":{"words":89,"sentences":12,"paragraphs":12},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-09-11-webinar-meetup.md","fields":{"slug":"/blog/2020-09-11-webinar-meetup/","keywords":["serverless","云函数","Serverless","备案","serverless","网站","腾讯","serverlesscloud"]}},"nextBlog":{"id":"635582b7-be92-5517-b7c6-0a1c5d2c6cde","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/202093/1599100692526-1598958654408-%E7%AC%AC%E4%B9%9D%E6%9C%9Fbanner.jpg","authors":["Serverless 社区"],"categories":["meetup"],"date":"2020-09-04T00:00:00.000Z","title":"消除冷启动，预置并发助力腾讯云 Serverless 云函数更快 | 在线分享第九期","description":"腾讯云云函数产品经理 Alfonso 为您分享！","authorslink":null,"translators":null,"translatorslink":null,"tags":["Serverless","Meetup"],"keywords":"Serverless CFS","outdated":null},"wordCount":{"words":36,"sentences":5,"paragraphs":5},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-09-04-webinar-meetup.md","fields":{"slug":"/blog/2020-09-04-webinar-meetup/","keywords":["serverless","云函数","并发","serverlesscloud","Serverless","冷启动","预置"]}},"recommendBlogs":{"edges":[{"node":{"id":"4300b21c-7209-5256-86ff-0d38e3daec9b","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/14f1c8eed372e76c1b139703b2f6d0fa.jpg","authors":["KieranMcCarthy"],"categories":["user-stories","engineering-culture"],"date":"2018-01-09T00:00:00.000Z","title":"我是如何在四年时间里，从厨师转行为 Serverless 应用开发者","description":"我是厨师出身，现在成为了一名 Serverless 应用开发者。","authorslink":["https://serverless.com/author/kieranmccarthy/"],"translators":["Aceyclee"],"translatorslink":["https://www.zhihu.com/people/Aceyclee"],"tags":["应用开发","Serverless"],"keywords":"Serverless 应用开发,Serverless 管理,厨师转行为 Serverless 应用开发者","outdated":null},"wordCount":{"words":285,"sentences":38,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-01-09-from-chef-to-serverless-developer-in-4-years.md","fields":{"slug":"/blog/2018-01-09-from-chef-to-serverless-developer-in-4-years/","keywords":["无服务器","无服务器开发","云函数","学习","Serverless","构建","Framework","开发者","服务器","应用","学位","简历"]}}},{"node":{"id":"713a0563-4bf9-5721-bacb-3b4ef609fe4a","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/camp-fire/camp-fire-housing-thumb.jpg","authors":["EricWyne"],"categories":["guides-and-tutorials","user-stories"],"date":"2018-12-05T00:00:00.000Z","title":"Serverless Twitter 机器人帮助为坎普山火受灾者安置住房","description":"加利福尼亚州的坎普山火致使数千人流离失所，为此，我构建了一个简单的 Serverless Twitter 机器人来帮助将受灾者安置在临时住房！","authorslink":["https://serverless.com/author/ericwyne/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":157,"sentences":26,"paragraphs":26},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-05-serverless-twitter-camp-fire.md","fields":{"slug":"/blog/2018-12-05-serverless-twitter-camp-fire/","keywords":["serverless","无服务器","云函数","Serverless","org","住房","Twitter","函数","受灾","机器人","山火"]}}},{"node":{"id":"98602143-b837-5f50-a24f-3b1ec76044d7","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/sqquid/sqquid-serverless-thumb.jpg","authors":["RonPeled"],"categories":["user-stories"],"date":"2018-12-17T00:00:00.000Z","title":"SQQUID：100% 无服务器初创公司","description":"SQQUID 将 AWS Lambda 和无服务器框架用于其核心产品和营销网站。我们来看看一个完全无服务器的初创公司是怎样的。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":266,"sentences":42,"paragraphs":42},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-17-sqquid-one-hundred-percent-serverless.md","fields":{"slug":"/blog/2018-12-17-sqquid-one-hundred-percent-serverless/","keywords":["go","serverless","无服务器","无服务器架构","服务器","架构","Lambda","集成","FaaS","串行","系统"]}}},{"node":{"id":"29dc2e58-d2ba-56f9-aee1-d21b0bc62e0e","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/ao-com-story/ao-serverless-thumbnail.png","authors":["NickGottlieb"],"categories":["user-stories"],"date":"2019-04-24T00:00:00.000Z","title":"AO.com：逐渐转向无服务器优先","description":"AO.com 的 SCV 团队率先尝试无服务器服务。折服于无服务器框架的快速周转时间和低维护成本，整个团队逐渐转向无服务器优先。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":236,"sentences":42,"paragraphs":35},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-04-24-ao-serverless-first.md","fields":{"slug":"/blog/2019-04-24-ao-serverless-first/","keywords":["serverless","无服务器","服务器","团队","Lambda","功能","构建"]}}},{"node":{"id":"752d08d1-387a-5bde-acf3-98141baab294","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586871710979-%E5%85%AC%E5%85%B1%E7%94%A8.png","authors":["Anycodes"],"categories":["user-stories"],"date":"2019-06-20T00:00:00.000Z","title":"如何用 Serverless 为 Python 云函数打包依赖","description":"在使用无服务器云函数SCF时通常会遇到导入第三方库的问题，很多小伙伴比较头疼是：应该如何打包进去？这里，推荐几个不错的方法。","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["云函数","Serverless"],"keywords":"Serverless,Serverless应用,无服务器云函数","outdated":null},"wordCount":{"words":81,"sentences":43,"paragraphs":43},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-06-20-for-python-cloud-functions.md","fields":{"slug":"/blog/2019-06-20-for-python-cloud-functions/","keywords":["java","serverless","无服务器","无服务器云函数","云函数","serverlesscloud","安装","serverless","pillowtest"]}}},{"node":{"id":"2dc78814-9d77-555b-a1bb-ad202c8ec2d1","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/cloudforecast/thumbnail.png","authors":["FrancoisLagier"],"categories":["user-stories"],"date":"2019-08-07T00:00:00.000Z","title":"Serverless：初创企业的理想选择？（CloudForecast 案例分析）","description":"CloudForecast 是 2018 年成立的一家独立初创企业，本文将介绍他们决定选择 Serverless 的原因。","authorslink":["https://serverless.com/author/francoislagier/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":211,"sentences":29,"paragraphs":29},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-08-07-serverless-for-startups.md","fields":{"slug":"/blog/2019-08-07-serverless-for-startups/","keywords":["serverless","云函数","serverless","函数","Serverless","utm","Framework","blog","CloudForecast","cloudforecast"]}}},{"node":{"id":"97450b07-658b-5207-8216-1c7b9b51b115","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578988490344-v2-8b2cd2c5275aa2c5a3c5083a148a7a9f_1200x500.jpg","authors":["Anycodes"],"categories":["user-stories"],"date":"2019-09-01T00:00:00.000Z","title":"如何通过 Serverless 与自然语言处理，让搜索引擎「看」到你的博客","description":"Serverless 与自然语言处理结合的一个小应用","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["个人博客","serverless"],"keywords":"Serverless 自然语言处理","outdated":null},"wordCount":{"words":106,"sentences":34,"paragraphs":34},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-09-01-search-engine-blog.md","fields":{"slug":"/blog/2019-09-01-search-engine-blog/","keywords":["serverless","云函数","keywords","serverlesscloud","summary"]}}},{"node":{"id":"ae4fd2f8-515c-5aec-b584-38427ef33f7e","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578989800047-part-00492-780.jpg","authors":["Anycodes"],"categories":["guides-and-tutorials","user-stories"],"date":"2019-09-16T00:00:00.000Z","title":"突破传统 OJ 瓶颈，「判题姬」接入云函数","description":"通过 Serverless 实现在线编程","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["在线编程","云函数"],"keywords":"Serverless 在线编程,Serverless OJ","outdated":null},"wordCount":{"words":169,"sentences":30,"paragraphs":30},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-09-16-online-Judge.md","fields":{"slug":"/blog/2019-09-16-online-Judge/","keywords":["python","serverless","云函数","代码","函数","serverless"]}}}],"totalCount":64}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"559c2b50-dcca-543e-aa11-a4b77232fd22","previousBlogId":"d1aff9e6-c8bf-5f1c-bcba-eb69158b1825","nextBlogId":"635582b7-be92-5517-b7c6-0a1c5d2c6cde","categories":["user-stories"]}}}