如何用 Serverless 優雅地給網站圖片加水印

不少論壇、博客在進行圖片上傳以後,都會給本身的圖像加上水印,這樣能夠證實這張圖片「屬於我」或者是「來自個人博客/網站」。那麼使用 Serverless 技術來加水印的方法比傳統方法好在哪兒呢,本文將對此進行一個簡單的分享。

傳統的加水印的方法,一般是在流程內進行。即:html

傳統方法

這種作法雖然可行,可是無疑會增長單次請求,服務端的壓力,若是是高併發的狀況下,或者多人上傳多張大圖的時候,那麼可能就會形成自身服務器資源效果過大。git

若是在加水印過程當中失敗,就有可能致使圖像存儲失敗,導致數據丟失,並不理智。因此後來有人作了以下改進:github

傳統方法改進版

這樣作法的好處就是 —— 咱們能夠快速將圖片存儲,存儲以後經過一個單獨處理的線程,對任務列表進行處理,這樣一方面是能夠保證咱們立刻把用戶上傳的圖片存儲,而且能夠顯示,同時也能夠在後臺進行水印處理,待處理以後,再將圖片覆蓋或者單獨存儲,用戶若是須要讀取圖片時,能夠自動變爲已經水印後的圖片。數組

這種作法相對於前者來講可能稍微複雜一些,可是實際上倒是在數據上更加穩定,服務端壓力更小,更加可控的一種操做。可是,這整個流程仍是要在本身的服務器上作完,如今已經有不少人將圖片等資源存儲到騰訊雲的 對象存儲(COS)中,那麼可不能夠經過某些 COS 觸發器與雲函數 SCF 結合,實現一個不在本身服務器加水印的流程呢?服務器

本文將以騰訊雲函數 SCF 的函數模板(Python 語言)爲例,進行一個簡單的分享。數據結構

▎實驗

新建函數

在無服務器雲函數中,選擇模板函數:架構

新建函數

經過搜索「圖像」關鍵詞,選中圖像壓縮,而且肯定創建。保存以後,點擊函數代碼,進行代碼編寫。併發

編寫代碼

COS 觸發器

有些人可能對 COS 觸發器還不是很瞭解,此時能夠點擊配置,來熟悉 COS 觸發器樣式:app

觸發器

能夠看到以下:less

{
   "Records":[
      {
        "event": {
          "eventVersion":"1.0",
          "eventSource":"qcs::cos",
          "eventName":"cos: ObjectCreated: *",
          "eventTime":1501054710,
          "eventQueue":"qcs:0:cos:gz:1251111111:cos",
          "requestParameters":{
            "requestSourceIP": "111.111.111.111",
            "requestHeaders":{
              "Authorization": "上傳的鑑權信息"
            }
          }
         },
         "cos":{
            "cosSchemaVersion":"1.0",
            "cosNotificationId":"設置的或返回的 ID",
            "cosBucket":{
               "name":"bucketname",
               "appid":"appId",
               "region":"gz"
            },
            "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"
            }
         }
      }
   ]
}

這裏面能夠看到整個一個數據結構,須要注意 Records 是一個數組格式,其次就是:

"cosBucket":{"name":"bucketname","appid":"appId","region":"gz"}

這裏面是由該 bucket 觸發

"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"}

這裏面的 key 是在上述 bucket 中新建的文件名字。

因此,咱們能夠按照咱們的實際狀況,將上面的內容簡單修改一下,成爲咱們格式,以便測試(在生產中,這個是自動生成的觸發格式,並不須要咱們修改,咱們修改只是爲了測試.

{
   "Records":[
      {
        "event": {
          "eventVersion":"1.0",
          "eventSource":"qcs::cos",
          "eventName":"cos: ObjectCreated: *",
          "eventTime":1501054710,
          "eventQueue":"qcs:0:cos:gz:1251111111:cos",
          "requestParameters":{
            "requestSourceIP": "111.111.111.111",
            "requestHeaders":{
              "Authorization": "上傳的鑑權信息"
            }
          }
         },
         "cos":{
            "cosSchemaVersion":"1.0",
            "cosNotificationId":"設置的或返回的 ID",
            "cosBucket":{
               "name":"mytestcos",
               "appid":"appId",
               "region":"gz"
            },
            "cosObject":{
               "key":"test.png",
               "size":2598526,
               "meta":{
                 "Content-Type": "text/plain",
                 "x-cos-meta-test": "自定義的 meta",
                 "x-image-test": "自定義的 meta"
               },
               "url": "訪問文件的源站url"
            }
         }
      }
   ]
}

這裏主要修改了個人 cosBucket-name: mytestcos,以及 key: test.png

cos 修改

代碼修改

之因此使用現有的模板,是由於該模板的有 PIL 和 qcloud_cos_v5 等相關 package,而這兩個 package 正是咱們即將須要的,這樣就能夠省去咱們打包函數的流程,只須要進行簡單修改便可實現。

添加水印:

def add_word(pic_path, save_path):
    # 打開圖片
    im = Image.open(pic_path).convert('RGBA')
    # 新建一個空白圖片,尺寸與打開圖片同樣
    txt = Image.new('RGBA', im.size, (0, 0, 0, 0))
    # 設置字體
    fnt = ImageFont.truetype("/tmp/font.ttf", 40)
    # 操做新建的空白圖片>>將新建的圖片添入畫板
    d = ImageDraw.Draw(txt)
    # 在新建的圖片上添加字體
    d.text((txt.size[0] - 220, txt.size[1] - 80), "By Dfounder", font=fnt,  fill=(255, 255, 255, 255))
    # 合併兩個圖片
    out = Image.alpha_composite(im, txt)
    # 保存圖像
    out.save(save_path)

在添加水印的時候,咱們設置的是文字水印,因此須要設置字體和字號:

fnt = ImageFont.truetype("/tmp/font.ttf",40)

此時,咱們須要在執行以前,先將字體文件傳入到 /tmp/ 文件夾下:

`response = client.get_object(Bucket="mytestcos-12567**", Key="font.ttf", )
response['Body'].get_stream_to_file('/tmp/font.ttf')`

以個人 cos 爲例:

例子

而後,接下來就是對觸發的 event 進行解析,包括得到新建的圖像名稱,從 COS 拉取,放到本地,而後進行水印等,再上傳回新的 COS 中:

for record in event['Records']:
        try:
            bucket = record['cos']['cosBucket']['name'] + '-' + str(appid)
            key = record['cos']['cosObject']['key']
            key = key.replace('/' + str(appid) + '/' + record['cos']['cosBucket']['name'] + '/', '', 1)
            download_path = '/tmp/{}'.format(key)
            upload_path = '/tmp/new_pic-{}'.format(key)

            # 下載圖片
            try:
                response = client.get_object(Bucket=bucket, Key=key, )
                response['Body'].get_stream_to_file(download_path)
            except CosServiceError as e:
                print(e.get_error_code())
                print(e.get_error_msg())
                print(e.get_resource_location())

            # 圖像增長水印
            add_word(download_path, upload_path)


            # 圖像上傳
            response = client.put_object_from_local_file(
                Bucket=to_bucket,
                LocalFilePath=upload_path.decode('utf-8'),
                Key=("upload-" + key).decode('utf-8')
            )

        except Exception as e:
            print(e)

此處說明一下,爲何要有兩個存儲桶?

由於咱們要把一個存儲桶做爲觸發 SCF 函數的「工具」,若是咱們將水印結果再次寫回這個存儲桶,在不進行額外判斷和處理的前提下,那麼這個水印後的圖片會再次水印,反反覆覆形成惡劣的循環,因此此處創建兩個存儲桶,能夠下降難度,也能夠保護性能,減小 BUG 誕生。

完整代碼以下:

# -*- coding: utf-8 -*-

from PIL import Image, ImageFont, ImageDraw
from qcloud_cos_v5 import CosConfig
from qcloud_cos_v5 import CosS3Client
from qcloud_cos_v5 import CosServiceError
from qcloud_cos_v5 import CosClientError

appid = **  # 請替換爲您的 APPID
secret_id = ***'  # 請替換爲您的 SecretId
secret_key = **'  # 請替換爲您的 SecretKey
region = u'ap-chengdu'  # 請替換爲您bucket 所在的地域
token = ''
to_bucket = 'tobucket-12567***'  # 請替換爲您用於存放壓縮後圖片的bucket

config = CosConfig(Secret_id=secret_id, Secret_key=secret_key, Region=region, Token=token)
client = CosS3Client(config)

response = client.get_object(Bucket="mytestcos-12567***", Key="font.ttf", )
response['Body'].get_stream_to_file('/tmp/font.ttf')

def add_word(pic_path, save_path):
    # 打開圖片
    im = Image.open(pic_path).convert('RGBA')
    # 新建一個空白圖片,尺寸與打開圖片同樣
    txt = Image.new('RGBA', im.size, (0, 0, 0, 0))
    # 設置字體
    fnt = ImageFont.truetype("/tmp/font.ttf", 40)
    # 操做新建的空白圖片>>將新建的圖片添入畫板
    d = ImageDraw.Draw(txt)
    # 在新建的圖片上添加字體
    d.text((txt.size[0] - 220, txt.size[1] - 80), "By Dfounder", font=fnt,  fill=(255, 255, 255, 255))
    # 合併兩個圖片
    out = Image.alpha_composite(im, txt)
    # 保存圖像
    out.save(save_path)

def main_handler(event, context):
    for record in event['Records']:
        try:
            bucket = record['cos']['cosBucket']['name'] + '-' + str(appid)
            key = record['cos']['cosObject']['key']
            key = key.replace('/' + str(appid) + '/' + record['cos']['cosBucket']['name'] + '/', '', 1)
            download_path = '/tmp/{}'.format(key)
            upload_path = '/tmp/new_pic-{}'.format(key)

            # 下載圖片
            try:
                response = client.get_object(Bucket=bucket, Key=key, )
                response['Body'].get_stream_to_file(download_path)
            except CosServiceError as e:
                print(e.get_error_code())
                print(e.get_error_msg())
                print(e.get_resource_location())

            # 圖像增長水印
            add_word(download_path, upload_path)


            # 圖像上傳
            response = client.put_object_from_local_file(
                Bucket=to_bucket,
                LocalFilePath=upload_path.decode('utf-8'),
                Key=("upload-" + key).decode('utf-8')
            )

        except Exception as e:
            print(e)

這裏面須要注意這幾個參數:appid、secret_id、secret_key、to_bucket

這幾個參數的來源以下:

參數來源

而 secretid,secretkey 則須要在這裏獲取:

id 獲取

測試

以前我已經上傳了一個測試圖片在這個 bucket 中,名字是:test.png

test 圖片

圖片是這樣子:

test 圖片 2

而後咱們進行一下測試:

測試

能夠看到,已經測試成功,接下來咱們能夠去咱們的目標 bucket 中看看:

目標 bucket

能夠看到成功生成了一個圖片:

生成圖片

能夠看到圖片的右下角,有咱們代碼中添加的水印:

水印代碼

至此,咱們完成了經過雲函數 SCF 爲您的網站圖片添加水印的基本流程。

▎額外想說的

其實,這篇文章也算是拋磚引玉,你們不只能夠經過這個流程壓縮圖像、添加水印,甚至還能夠對圖像進行其餘操做。例如圖像風格化處理等一些深加工,而這一切過程,都不會佔用自身服務器資源,而是經過雲函數 SCF 來完成的。

即便面對高併發,有大量的圖片上傳時,咱們要作的也僅僅是經過 SDK,將圖片傳入到騰訊雲對象存儲 COS 中。

傳送門:

歡迎訪問:Serverless 中文網,您能夠在 最佳實踐 裏體驗更多關於 Serverless 應用的開發!


推薦閱讀: 《Serverless 架構:從原理、設計到項目實戰》
相關文章
相關標籤/搜索