{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2019-10-08-pic-watermark","result":{"data":{"currentBlog":{"id":"314ba9ca-ca70-5059-a575-036572744b65","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578990760585-v2-7d9090260d7f4a17539902a2c1ef8264_r.jpg","authors":["Anycodes"],"categories":["guides-and-tutorials","user-stories"],"date":"2019-10-08T00:00:00.000Z","title":"如何优雅地给网站图片加水印","description":"Serverless 在 Web 后端与应用服务端的应用","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["图像处理","Serverless"],"keywords":"Serverless 应用服务端,Serverless 应用,Serverless Web 后端","outdated":null},"wordCount":{"words":276,"sentences":66,"paragraphs":66},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-10-08-pic-watermark.md","fields":{"slug":"/blog/2019-10-08-pic-watermark/","keywords":["serverless","无服务器","无服务器云函数","云函数","cos","图片","serverlesscloud"]},"html":"<blockquote>\n<p>很多论坛、博客在进行图片上传之后，都会给自己的图像加上水印，这样可以证明这张图片「属于我」或者是「来自我的博客/网站」。那么使用 Serverless 技术来加水印的方法比传统方法好在哪儿呢，本文将对此进行一个简单的分享。</p>\n</blockquote>\n<p>传统的加水印的方法，通常是在流程内进行。即：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849743-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"传统方法\"></p>\n<p>这种做法虽然可行，但是无疑会增加单次请求，服务端的压力，如果是高并发的情况下，或者多人上传多张大图的时候，那么可能就会造成自身服务器资源效果过大。</p>\n<p>如果在加水印过程中失败，就有可能导致图像存储失败，致使数据丢失，并不理智。所以后来有人做了如下改进：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849939-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"传统方法改进版\"></p>\n<p>这样做法的好处就是 —— 我们可以快速将图片存储，存储之后通过一个单独处理的线程，对任务列表进行处理，这样一方面是可以保证我们马上把用户上传的图片存储，并且可以显示，同时也可以在后台进行水印处理，待处理之后，再将图片覆盖或者单独存储，用户如果需要读取图片时，可以自动变为已经水印后的图片。</p>\n<p>这种做法相对于前者来说可能稍微复杂一些，但是实际上却是在数据上更加稳定，服务端压力更小，更加可控的一种操作。但是，这整个流程还是要在自己的服务器上做完，现在已经有很多人将图片等资源存储到腾讯云的 <a href=\"https://cloud.tencent.com/product/cos?from=10680\">对象存储（COS）</a>中，那么可不可以通过某些 COS 触发器与云函数 SCF 结合，实现一个不在自己服务器加水印的流程呢？</p>\n<p>本文将以腾讯云函数 SCF 的函数模板（Python 语言）为例，进行一个简单的分享。</p>\n<h2 id=\"▎实验\"><a href=\"#%E2%96%8E%E5%AE%9E%E9%AA%8C\" 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<h4 id=\"新建函数\"><a href=\"#%E6%96%B0%E5%BB%BA%E5%87%BD%E6%95%B0\" 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>新建函数</h4>\n<p>在无服务器云函数中，选择模板函数：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849988-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"新建函数\"></p>\n<p>通过搜索「图像」关键词，选中图像压缩，并且确定建立。保存之后，点击函数代码，进行代码编写。</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849634-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"编写代码\"></p>\n<h4 id=\"cos-触发器\"><a href=\"#cos-%E8%A7%A6%E5%8F%91%E5%99%A8\" aria-label=\"cos 触发器 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>COS 触发器</h4>\n<p>有些人可能对 COS 触发器还不是很了解，此时可以点击配置，来熟悉 COS 触发器样式：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849769-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"触发器\"></p>\n<p>可以看到如下：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">{\n   &quot;Records&quot;:[\n      {\n        &quot;event&quot;: {\n          &quot;eventVersion&quot;:&quot;1.0&quot;,\n          &quot;eventSource&quot;:&quot;qcs::cos&quot;,\n          &quot;eventName&quot;:&quot;cos: ObjectCreated: *&quot;,\n          &quot;eventTime&quot;:1501054710,\n          &quot;eventQueue&quot;:&quot;qcs:0:cos:gz:1251111111:cos&quot;,\n          &quot;requestParameters&quot;:{\n            &quot;requestSourceIP&quot;: &quot;111.111.111.111&quot;,\n            &quot;requestHeaders&quot;:{\n              &quot;Authorization&quot;: &quot;上传的鉴权信息&quot;\n            }\n          }\n         },\n         &quot;cos&quot;:{\n            &quot;cosSchemaVersion&quot;:&quot;1.0&quot;,\n            &quot;cosNotificationId&quot;:&quot;设置的或返回的 ID&quot;,\n            &quot;cosBucket&quot;:{\n               &quot;name&quot;:&quot;bucketname&quot;,\n               &quot;appid&quot;:&quot;appId&quot;,\n               &quot;region&quot;:&quot;gz&quot;\n            },\n            &quot;cosObject&quot;:{\n               &quot;key&quot;:&quot;/appid/bucketname/DSC_0002.JPG&quot;,\n               &quot;size&quot;:2598526,\n               &quot;meta&quot;:{\n                 &quot;Content-Type&quot;: &quot;text/plain&quot;,\n                 &quot;x-cos-meta-test&quot;: &quot;自定义的 meta&quot;,\n                 &quot;x-image-test&quot;: &quot;自定义的 meta&quot;\n               },\n               &quot;url&quot;: &quot;访问文件的源站url&quot;\n            }\n         }\n      }\n   ]\n}</code></pre></div>\n<p>这里面可以看到整个一个数据结构，需要注意 Records 是一个数组格式，其次就是：</p>\n<p>\"cosBucket\":{\"name\":\"bucketname\",\"appid\":\"appId\",\"region\":\"gz\"}</p>\n<p>这里面是由该 bucket 触发</p>\n<p>\"cosObject\":{\"key\":\"/appid/bucketname/DSC_0002.JPG\",\"size\":2598526,\"meta\":{\"Content-Type\":\"text/plain\",\"x-cos-meta-test\":\"自定义的 meta\",\"x-image-test\":\"自定义的 meta\"},\"url\":\"访问文件的源站 url\"}</p>\n<p>这里面的 key 是在上述 bucket 中新建的文件名字。</p>\n<p>所以，我们可以按照我们的实际情况，将上面的内容简单修改一下，成为我们格式，以便测试（在生产中，这个是自动生成的触发格式，并不需要我们修改，我们修改只是为了测试.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">{\n   &quot;Records&quot;:[\n      {\n        &quot;event&quot;: {\n          &quot;eventVersion&quot;:&quot;1.0&quot;,\n          &quot;eventSource&quot;:&quot;qcs::cos&quot;,\n          &quot;eventName&quot;:&quot;cos: ObjectCreated: *&quot;,\n          &quot;eventTime&quot;:1501054710,\n          &quot;eventQueue&quot;:&quot;qcs:0:cos:gz:1251111111:cos&quot;,\n          &quot;requestParameters&quot;:{\n            &quot;requestSourceIP&quot;: &quot;111.111.111.111&quot;,\n            &quot;requestHeaders&quot;:{\n              &quot;Authorization&quot;: &quot;上传的鉴权信息&quot;\n            }\n          }\n         },\n         &quot;cos&quot;:{\n            &quot;cosSchemaVersion&quot;:&quot;1.0&quot;,\n            &quot;cosNotificationId&quot;:&quot;设置的或返回的 ID&quot;,\n            &quot;cosBucket&quot;:{\n               &quot;name&quot;:&quot;mytestcos&quot;,\n               &quot;appid&quot;:&quot;appId&quot;,\n               &quot;region&quot;:&quot;gz&quot;\n            },\n            &quot;cosObject&quot;:{\n               &quot;key&quot;:&quot;test.png&quot;,\n               &quot;size&quot;:2598526,\n               &quot;meta&quot;:{\n                 &quot;Content-Type&quot;: &quot;text/plain&quot;,\n                 &quot;x-cos-meta-test&quot;: &quot;自定义的 meta&quot;,\n                 &quot;x-image-test&quot;: &quot;自定义的 meta&quot;\n               },\n               &quot;url&quot;: &quot;访问文件的源站url&quot;\n            }\n         }\n      }\n   ]\n}</code></pre></div>\n<p>这里主要修改了我的 cosBucket-name: mytestcos，以及 key: test.png</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849468-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"cos 修改\"></p>\n<h4 id=\"代码修改\"><a href=\"#%E4%BB%A3%E7%A0%81%E4%BF%AE%E6%94%B9\" 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>代码修改</h4>\n<p>之所以使用现有的模板，是因为该模板的有 PIL 和 qcloud<em>cos</em>v5 等相关 package，而这两个 package 正是我们即将需要的，这样就可以省去我们打包函数的流程，只需要进行简单修改即可实现。</p>\n<p>添加水印：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">def add_word(pic_path, save_path):\n    # 打开图片\n    im = Image.open(pic_path).convert(&#39;RGBA&#39;)\n    # 新建一个空白图片,尺寸与打开图片一样\n    txt = Image.new(&#39;RGBA&#39;, im.size, (0, 0, 0, 0))\n    # 设置字体\n    fnt = ImageFont.truetype(&quot;/tmp/font.ttf&quot;, 40)\n    # 操作新建的空白图片&gt;&gt;将新建的图片添入画板\n    d = ImageDraw.Draw(txt)\n    # 在新建的图片上添加字体\n    d.text((txt.size[0] - 220, txt.size[1] - 80), &quot;By Dfounder&quot;, font=fnt,  fill=(255, 255, 255, 255))\n    # 合并两个图片\n    out = Image.alpha_composite(im, txt)\n    # 保存图像\n    out.save(save_path)</code></pre></div>\n<p>在添加水印的时候，我们设置的是文字水印，所以需要设置字体和字号：</p>\n<p><code class=\"language-text\">fnt = ImageFont.truetype(&quot;/tmp/font.ttf&quot;,40)</code></p>\n<p>此时，我们需要在执行之前，先将字体文件传入到 /tmp/ 文件夹下：</p>\n<p><code class=\"language-text\">response = client.get_object(Bucket=&quot;mytestcos-12567****&quot;, Key=&quot;font.ttf&quot;, )\nresponse[&#39;Body&#39;].get_stream_to_file(&#39;/tmp/font.ttf&#39;)</code></p>\n<p>以我的 cos 为例：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578991065314-v2-676a50ad79018e0ab79bcb786c670c0f_hd.jpg\" alt=\"例子\"></p>\n<p>然后，接下来就是对触发的 event 进行解析，包括获得新建的图像名称，从 COS 拉取，放到本地，然后进行水印等，再上传回新的 COS 中：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">for record in event[&#39;Records&#39;]:\n        try:\n            bucket = record[&#39;cos&#39;][&#39;cosBucket&#39;][&#39;name&#39;] + &#39;-&#39; + str(appid)\n            key = record[&#39;cos&#39;][&#39;cosObject&#39;][&#39;key&#39;]\n            key = key.replace(&#39;/&#39; + str(appid) + &#39;/&#39; + record[&#39;cos&#39;][&#39;cosBucket&#39;][&#39;name&#39;] + &#39;/&#39;, &#39;&#39;, 1)\n            download_path = &#39;/tmp/{}&#39;.format(key)\n            upload_path = &#39;/tmp/new_pic-{}&#39;.format(key)\n\n            # 下载图片\n            try:\n                response = client.get_object(Bucket=bucket, Key=key, )\n                response[&#39;Body&#39;].get_stream_to_file(download_path)\n            except CosServiceError as e:\n                print(e.get_error_code())\n                print(e.get_error_msg())\n                print(e.get_resource_location())\n\n            # 图像增加水印\n            add_word(download_path, upload_path)\n\n\n            # 图像上传\n            response = client.put_object_from_local_file(\n                Bucket=to_bucket,\n                LocalFilePath=upload_path.decode(&#39;utf-8&#39;),\n                Key=(&quot;upload-&quot; + key).decode(&#39;utf-8&#39;)\n            )\n\n        except Exception as e:\n            print(e)</code></pre></div>\n<p>此处说明一下，为什么要有两个存储桶？</p>\n<p>因为我们要把一个存储桶作为触发 SCF 函数的「工具」，如果我们将水印结果再次写回这个存储桶，在不进行额外判断和处理的前提下，那么这个水印后的图片会再次水印，反反复复造成恶劣的循环，所以此处建立两个存储桶，可以降低难度，也可以保护性能，减少 BUG 诞生。</p>\n<p>完整代码如下：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># -*- coding: utf-8 -*-\n\nfrom PIL import Image, ImageFont, ImageDraw\nfrom qcloud_cos_v5 import CosConfig\nfrom qcloud_cos_v5 import CosS3Client\nfrom qcloud_cos_v5 import CosServiceError\nfrom qcloud_cos_v5 import CosClientError\n\nappid = **  # 请替换为您的 APPID\nsecret_id = ***&#39;  # 请替换为您的 SecretId\nsecret_key = **&#39;  # 请替换为您的 SecretKey\nregion = u&#39;ap-chengdu&#39;  # 请替换为您bucket 所在的地域\ntoken = &#39;&#39;\nto_bucket = &#39;tobucket-12567***&#39;  # 请替换为您用于存放压缩后图片的bucket\n\nconfig = CosConfig(Secret_id=secret_id, Secret_key=secret_key, Region=region, Token=token)\nclient = CosS3Client(config)\n\nresponse = client.get_object(Bucket=&quot;mytestcos-12567***&quot;, Key=&quot;font.ttf&quot;, )\nresponse[&#39;Body&#39;].get_stream_to_file(&#39;/tmp/font.ttf&#39;)\n\ndef add_word(pic_path, save_path):\n    # 打开图片\n    im = Image.open(pic_path).convert(&#39;RGBA&#39;)\n    # 新建一个空白图片,尺寸与打开图片一样\n    txt = Image.new(&#39;RGBA&#39;, im.size, (0, 0, 0, 0))\n    # 设置字体\n    fnt = ImageFont.truetype(&quot;/tmp/font.ttf&quot;, 40)\n    # 操作新建的空白图片&gt;&gt;将新建的图片添入画板\n    d = ImageDraw.Draw(txt)\n    # 在新建的图片上添加字体\n    d.text((txt.size[0] - 220, txt.size[1] - 80), &quot;By Dfounder&quot;, font=fnt,  fill=(255, 255, 255, 255))\n    # 合并两个图片\n    out = Image.alpha_composite(im, txt)\n    # 保存图像\n    out.save(save_path)\n\ndef main_handler(event, context):\n    for record in event[&#39;Records&#39;]:\n        try:\n            bucket = record[&#39;cos&#39;][&#39;cosBucket&#39;][&#39;name&#39;] + &#39;-&#39; + str(appid)\n            key = record[&#39;cos&#39;][&#39;cosObject&#39;][&#39;key&#39;]\n            key = key.replace(&#39;/&#39; + str(appid) + &#39;/&#39; + record[&#39;cos&#39;][&#39;cosBucket&#39;][&#39;name&#39;] + &#39;/&#39;, &#39;&#39;, 1)\n            download_path = &#39;/tmp/{}&#39;.format(key)\n            upload_path = &#39;/tmp/new_pic-{}&#39;.format(key)\n\n            # 下载图片\n            try:\n                response = client.get_object(Bucket=bucket, Key=key, )\n                response[&#39;Body&#39;].get_stream_to_file(download_path)\n            except CosServiceError as e:\n                print(e.get_error_code())\n                print(e.get_error_msg())\n                print(e.get_resource_location())\n\n            # 图像增加水印\n            add_word(download_path, upload_path)\n\n\n            # 图像上传\n            response = client.put_object_from_local_file(\n                Bucket=to_bucket,\n                LocalFilePath=upload_path.decode(&#39;utf-8&#39;),\n                Key=(&quot;upload-&quot; + key).decode(&#39;utf-8&#39;)\n            )\n\n        except Exception as e:\n            print(e)</code></pre></div>\n<p>这里面需要注意这几个参数：appid、secret<em>id、secret</em>key、to_bucket</p>\n<p>这几个参数的来源如下：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849480-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"参数来源\"></p>\n<p>而 secretid，secretkey 则需要在这里获取：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849750-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"id 获取\"></p>\n<h4 id=\"测试\"><a href=\"#%E6%B5%8B%E8%AF%95\" 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>测试</h4>\n<p>之前我已经上传了一个测试图片在这个 bucket 中，名字是：test.png</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849100-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"test 图片\"></p>\n<p>图片是这样子：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849099-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"test 图片 2\"></p>\n<p>然后我们进行一下测试：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849528-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"测试\"></p>\n<p>可以看到，已经测试成功，接下来我们可以去我们的目标 bucket 中看看：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849101-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"目标 bucket\"></p>\n<p>可以看到成功生成了一个图片：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849857-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"生成图片\"></p>\n<p>可以看到图片的右下角，有我们代码中添加的水印：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020114/1578990849632-v2-662ba51a47abe18f335bce253dea244e_hd.png\" alt=\"水印代码\"></p>\n<p>至此，我们完成了通过云函数 SCF 为您的网站图片添加水印的基本流程。</p>\n<h2 id=\"▎额外想说的\"><a href=\"#%E2%96%8E%E9%A2%9D%E5%A4%96%E6%83%B3%E8%AF%B4%E7%9A%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>其实，这篇文章也算是抛砖引玉，大家不仅可以通过这个流程压缩图像、添加水印，甚至还可以对图像进行其他操作。例如图像风格化处理等一些深加工，而这一切过程，都不会占用自身服务器资源，而是通过云函数 SCF 来完成的。</p>\n<p>即使面对高并发，有大量的图片上传时，我们要做的也仅仅是通过 SDK，将图片传入到腾讯云对象存储 COS 中。</p>\n<blockquote>\n<p><strong>传送门：</strong></p>\n<ul>\n<li>GitHub: <a href=\"https://github.com/serverless/serverless/blob/master/README_CN.md\">github.com/serverless</a> </li>\n<li>官网：<a href=\"https://serverless.com/\">serverless.com</a></li>\n</ul>\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>\n<p><a href=\"/blog/2019-10-08-pic-watermark/#%E2%96%8E%E5%AE%9E%E9%AA%8C\">▎实验</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/blog/2019-10-08-pic-watermark/#%E6%96%B0%E5%BB%BA%E5%87%BD%E6%95%B0\">新建函数</a></li>\n<li><a href=\"/blog/2019-10-08-pic-watermark/#cos-%E8%A7%A6%E5%8F%91%E5%99%A8\">COS 触发器</a></li>\n<li><a href=\"/blog/2019-10-08-pic-watermark/#%E4%BB%A3%E7%A0%81%E4%BF%AE%E6%94%B9\">代码修改</a></li>\n<li><a href=\"/blog/2019-10-08-pic-watermark/#%E6%B5%8B%E8%AF%95\">测试</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li><a href=\"/blog/2019-10-08-pic-watermark/#%E2%96%8E%E9%A2%9D%E5%A4%96%E6%83%B3%E8%AF%B4%E7%9A%84\">▎额外想说的</a></li>\n</ul>"},"previousBlog":{"id":"545ab3d2-e14e-5cc2-8548-0e863eac942b","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/2019-10-deployment-best-practices/safeguard-header.png","authors":["FernandoMedinaCorey"],"categories":["guides-and-tutorials"],"date":"2019-10-14T00:00:00.000Z","title":"无服务器部署最佳实践","description":"了解部署无服务器应用时的一些最佳实践。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":221,"sentences":46,"paragraphs":46},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-10-14-serverless-deployment-best-practices.md","fields":{"slug":"/blog/2019-10-14-serverless-deployment-best-practices/","keywords":["serverless","无服务器","serverless","部署","服务器","开发人员","应用","安全措施","使用","函数"]}},"nextBlog":{"id":"a391eb20-aeac-51c4-9330-3a3d18cb674f","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/qianyi/images/l7x0FXB2eW7ibWsCibiaAIeGFC7dLbCTXG7yYebTv238UXXPJCCravianRBePr9qeGGAEB0HRsBVLh5Nvggtq7P08A.jpg","authors":["周俊鹏"],"categories":["news"],"date":"2019-09-30T00:00:00.000Z","title":"Serverless —— 前端 3.0 时代","description":"《信息简史》中说“进化本身是生物体与环境之间持续不断的信息交换的具体表现”，前端技术的进化也是如此。浩瀚的前端宇宙中，又出现过哪些耀眼的星辰呢？指引前端未来的“北极星”又在何方？腾讯云高级工程师与你畅谈前端的变革史与新时代的希冀。","authorslink":["https://github.com/jiangliu5267"],"translators":null,"translatorslink":null,"tags":["云开发","云函数"],"keywords":"Serverless, Serverless前端开发, Serverless云函数","outdated":null},"wordCount":{"words":321,"sentences":62,"paragraphs":62},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-09-30-serverless-arch-function.md","fields":{"slug":"/blog/2019-09-30-serverless-arch-function/","keywords":["serverless","云函数","前端","Serverless","Node","React"]}},"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":"665f9ce2-4451-59fd-bf98-1861789d3b3b","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/Serverless_logo.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-03-19T00:00:00.000Z","title":"如何为无服务器开放源代码项目做贡献","description":"想要为无服务器开放源代码项目做贡献？您可以遵循下面的指南。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":96,"sentences":36,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-19-how-contribute-to-serverless-open-source.md","fields":{"slug":"/blog/2018-03-19-how-contribute-to-serverless-open-source/","keywords":["serverless","无服务器","serverless","github","插件","服务器","贡献","示例","blog","contribute"]}}},{"node":{"id":"a3e92579-65c3-5159-937c-32d18c5df7d7","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/why-not/why-not-header.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","operations-and-observability","engineering-culture"],"date":"2018-03-21T00:00:00.000Z","title":"不适合选择无服务器的情境及原因","description":"无服务器既有优点也有缺点。那么，哪些情境下不适合选择无服务器？原因又是什么呢？","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":163,"sentences":43,"paragraphs":43},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-21-when-why-not-use-serverless.md","fields":{"slug":"/blog/2018-03-21-when-why-not-use-serverless/","keywords":["faas","react","serverless","spa","无服务器","无服务器函数","无服务器架构","无服务器开发","服务器","twitter","serverless","blockquote","lang","script","en"]}}},{"node":{"id":"6a16520b-7886-582e-9182-64e50712d486","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/vendor+choice/serverless-data-portability.jpg","authors":["NickGottlieb"],"categories":["engineering-culture","guides-and-tutorials"],"date":"2018-06-20T00:00:00.000Z","title":"浅谈无服务器、数据锁定和供应商选择","description":"供应商选择是如今 IT 领导者需要考虑的最重要事项，而这一点可利用数据可移植性来实现。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":205,"sentences":33,"paragraphs":33},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-06-20-data-lockin-vendor-choice-portability.md","fields":{"slug":"/blog/2018-06-20-data-lockin-vendor-choice-portability/","keywords":["go","serverless","无服务器","无服务器架构","供应商","serverless","开发人员","数据","锁定","选择","服务"]}}},{"node":{"id":"94741abb-10ba-5db1-9756-cd1d573473fa","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/webstorm-ide/streamline-webstorm-serverless2.jpg","authors":["EslamHefnawy"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-08-15T00:00:00.000Z","title":"如何使用 WebStorm 简化无服务器工作流程","description":"在本文中，我将和您分享如何使用 WebStorm 进行无服务器特定的 IDE 设置以及如何利用它来极大地加快无服务器工作流程。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":234,"sentences":54,"paragraphs":54},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-08-15-streamline-serverless-workflow-webstorm.md","fields":{"slug":"/blog/2018-08-15-streamline-serverless-workflow-webstorm/","keywords":["nodejs","serverless","无服务器","无服务器开发","serverless","WebStorm","webstorm","服务器","blog","assets"]}}},{"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","功能","构建"]}}}],"totalCount":89}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"314ba9ca-ca70-5059-a575-036572744b65","previousBlogId":"545ab3d2-e14e-5cc2-8548-0e863eac942b","nextBlogId":"a391eb20-aeac-51c4-9330-3a3d18cb674f","categories":["guides-and-tutorials","user-stories"]}}}