技术原理

[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

官网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后再回传让浏览器知道资料库的状态已更新

你也可能喜欢

发表评论

您的电子邮件地址不会被公开。 必填项已用 * 标注

提示:点击验证后方可评论!

插入图片
人工智能ea交易系统 投稿者
我还没有学会写个人说明!
最近文章
  • * 没有更多文章了
  • 热门搜索

    分类目录