iT邦幫忙

0

[Node.js]實作multer檔案上傳(三)

本系列目標

ps(本系列文章將引用ToDo List的專案去演示,抱歉稍後補上專案GitHub連結)

這篇文章你將學習到

  • 使用者唱完圖片後,後端將圖片轉成base64傳至第三方托管(imgur),最後可以將圖片渲染在EJS上面。
    ps. 通常會比較正式的圖片託管平台是AWS的S3服務,在本篇以練習為主所以我們使用免費的imgur。

開始實作

概念

我們將使用multer(opt)方法,opt默認是使用記憶體(memory)來暫存檔案,就不寫入硬碟(disk)空間,但是要確保記憶體的空間是足夠存放的,通常multer會需要另外額外的空間,所以預設的空間要更大,後續才不會有疑慮。接著我們透過後端轉base64格式後,由後端發一個request打去imgur的API上傳圖片,最後我們將imgur回傳的圖片網址寫入到DB裡面。
第三方託管的好處是:

  • 每次使用者request圖片的時候,就會變成是跟imgur去要圖片,省下了原本服務器將圖片傳給使用者的流量。

調研imgur的API

調用imgur的API,會要求我們輸入的client ID當作憑證,我在google上查到很多相關取得client ID的中文教學,這步驟大家可以去google我就不多介紹了。

用Postman去調用測試imgur的API

imgur的官網API:
https://apidocs.imgur.com/?version=latest#intro

官網API文件中尋找Image類型,然後是Image Upload,http method是post,我們寫程式碼之前可以用Postman去做上傳測試。
官網很貼心地做了各種程式語言的request API的範本,就在最上方設定齒輪旁邊的Language,我自己是用Node JS,端看你們自己的程式語言做選擇。

調用imgur圖片上傳API有3個必填

1. Image Uploade的API網址

https://api.imgur.com/3/upload

2. 要上傳的圖片

A binary file, base64 data, or a URL for an image. (up to 10MB)
官網有定義,request的body其中一個欄位是欲上傳的圖片,key = image,value = 可以是二進位的檔案or base64的字串or一張圖片,大小不能超過10MB
https://ithelp.ithome.com.tw/upload/images/20200520/20121402ac2g9GgzVO.png

3. client-ID

填寫上key欄位寫Authorization,value欄位寫上client-ID空格(你的ID)
https://ithelp.ithome.com.tw/upload/images/20200520/20121402uVNgISogut.png

Postman按下send按鈕,調用成功!

上傳成功的話他,我們會接收到status 200成功的response,然後在link這個key上面會告訴我們這張圖片的網址是多少。
https://ithelp.ithome.com.tw/upload/images/20200520/20121402IQRHj29eMq.png

安裝request-promise

我們等等要用這套件去發request過去到imgur上傳圖片

npm i request-promise --save

設定存放圖片

這裡我們讓multer(opt)的opt不指定dest存放位置,multer()就會是默認的將圖片以buffer方式寫入記憶體

var upload = multer({
    fileFilter: (req, file, cb) => {
        if (file.mimetype == "image/png") {
            cb(null, true)
        } else {
            cb(null, false)
            return cb(new Error('Allowed only .png'))
        }
    }
})

處理接收到的圖片

routes/index.js

我們在post method的/uploadfile路由中放入upload當作middleware

router.post('/uploadfile', upload.single('myfile'), function (req, res) {
    //我們等等下面要做的圖片處理的程式碼
})

將圖片轉成base64

const encode_image = req.file.buffer.toString('base64')

接下來,我們要打imgur的API

先把引數寫好,檔案只能透過formData傳送,image的value就放我們要上傳的base64圖片,等一下就來寫執行request-promise的方法

 options = {
        'method': 'POST',
        'url': 'https://api.imgur.com/3/image',
        'headers': {
            'Authorization': 'Client-ID 62004dc8f2239f1'
        },
        formData: {
            'image': encode_image}
    };

執行發出requet

這裡我們會遇到非同步的問題,request()和db寫入這兩隻都是非同步的,所以我們要使用async/await等讓我們拿到圖片網址後,再執行寫入DB的工作,否則寫入DB的資料會是null

await request(options, function (error, response) {
            if (error) throw new Error(error);
            imgurURL = response.body
});

寫入DB

因為我們得到imgur的response是一個JSON string,所以我們必須要先轉為JSON

MongoClient.connect(url, async function (err, db) {
     const imgurURLToJSON2 = JSON.parse(imgurURL).data.link
}

然後再加上執行DB是寫入我們得到圖片的網址(url)

var dbo = db.db("todolist");
        dbo.collection("post").insertOne({image: imgurURLToJSON2}, function (err, res) {
            if (err) throw err;
            console.log("image URL inserted");
            db.close();
        });

在EJS渲染這張圖片

在views/index.ejs

 <img src="<%= doc.image %>"/>

完成

我們打開網址http://localhost:3000/

https://ithelp.ithome.com.tw/upload/images/20200518/20121402TsovdmeZvR.png

今日感想

雖然說加入第三方的圖片託管服務會多一段流量,即是使用者上傳圖片到後端服務器,另一個就是後端把該圖片轉base64後送到imgur平台去做上傳圖片。但是這也僅限在第一次,接下來圖片的流量都會是在imgur身上,我們後端只要負責把檔案大小/格式控制好,是一個可以分散流量的好方法。
可以繼續優化的點:

  • 後端可以處理圖片壓縮,畫面微調等...
  • 安全性來說,我們已經用後端去篩選使用者只能上傳png,更好的是我們可以限制使用者在時間內只能上傳幾張圖片,以避免惡意的把我們的圖片空間給塞爆
  • 把轉base64的動作的動作寫作一個Task,加入排程中,上傳完後前端會讓使用者以為成功了,然後非阻塞的繼續瀏覽網站,等待成功寫入DB後再回傳讓瀏覽器知道資料庫的狀態已更新

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言