ทำระบบอัพโหลดไฟล์ด้วย Node.js + Multer

วิธีการทำระบบไฟล์อัพโหลดด้วยการใช้ Node.js + Express และ Multer ซึ่งบทความนี้เป็นบทความอัพเดท จากที่เคยเขียนไว้ก่อนหน้านี้ 7-8 แล้วครับ ทำระบบอัพโหลดไฟล์ด้วย Node.js ซึ่งตัว Library มันก็มีการอัพเดทไปพอสมควร
Step 1 - Create Project
เริ่มต้นด้วยการสร้างโปรเจ็คขึ้นมาโดยใช้ npm init
หรือ yarn init
ก็ได้
yarn init -y
จากนั้นก็ทำการติดตั้ง express และ multer
yarn add express multer
Step 2 - Frontend (index.html)
ต่อมาสร้างไฟล์ index.html
ขึ้นมา โดยส่วนนี้จะเป็นหน้าเว็บธรรมดา ที่มีแค่การอัพโหลดไฟล์ โดยใช้ <input type="file" />
<!doctype html><html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Hello Multer</title> </head> <body> <h1>Hello Multer</h1> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="photo" /> <button type="submit" value="Upload" /> </form> </body></html>
โดยที่ตัว form ต้องกำหนด enctype
เป็น multipart/form-data
Step 3 - Create Server
สร้าง app.js
ขึ้นมา เพื่อเป็น Server ง่ายๆ โดยให้ index ทำการอ่านจากไฟล์ index.html
ที่เราสร้างไว้้ และ POST /upload
เพื่อไว้รับค่า จากไฟล์ที่เราต้องการอัพโหลด
// ไฟล์ app.jsconst express = require('express')const app = express()
app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html')})
app.post('/upload', (req, res) => { res.json(req.body)})
app.listen(9999, () => console.log('Running on port 9999'))
ทดสอบ โดยการ start server
node app.js
ทดสอบเรียก request ไปที่ POST /upload
เพื่อดูผลลัพธ์
curl -L -X POST 'http://localhost:9999/upload' \-H 'Content-Type: application/json' \-D '{"message": "Hello"}'
หรือทดสอบด้วย Postman
จะเห็นว่าเราจะไม่ได้ผลลัพธ์อะไร แม้ว่าเราจะให้มัน res.json(req.body)
ก็ตาม เพราะเนื่องจาก โดย default ตัว Express ไม่สามารถอ่าน req.body
ได้ ถ้าอยากให้อ่านได้ คือใช้ body-parser หรือใช้ตัว express.json()
แบบนี้
const express = require('express')const app = express()
app.use(express.json())
Restart และทดสอบ POST Request ไปใหม่ จะเห็นว่าเราสามารถอ่านค่า req.body
ได้แล้ว
Step 4- Multer
ตัว multer จะมาช่วยจัดการเรื่องของไฟล์ โดยที่เราจะสามารถเข้าถึง object ไฟล์ client ส่งมา ด้วย req.file
และตัว req.body
ตัว multer ก็ทำการ handle ให้เราแล้ว ฉะนั้น ก็ไม่จำเป็นต้องใช้ body-parser เราก็สามารถเข้าถึงได้ทั้ง req.file
และ req.body
const multer = require('multer')const upload = multer({ dest: 'uploads' })
โค๊ดด้านบนเป็นการกำหนด multer และให้โฟลเดอร์ที่เราจะเก็บไฟล์คือ uploads
สิ่งที่ต้องรู้คือ
upload.single
: จะอัพโหลดได้ทีละไฟล์- ตัว
upload.single('photo')
ชื่อที่เรากำหนด ต้องเป็นชื่อเดียวกับ form ของเรา<input name="photo" />
const express = require('express')const multer = require('multer')
const app = express()
const upload = multer({ dest: 'uploads' })
app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html')})
app.post('/upload', upload.single('photo'), (req, res) => { res.send(req.file)})
app.listen(9999, () => console.log('Running on port 9999'))
ทดลองเปิดหน้าเว็บ http://localhost:9999 จากนั้นเลือกไฟล์แล้วคลิ๊ก Upload ดูครับ จะได้ผลลัพธ์ประมาณนี้
{ "fieldname": "photo", "originalname": "Screen Shot 2565-07-14 at 22.26.37.png", "encoding": "7bit", "mimetype": "image/png", "destination": "uploads", "filename": "801ea9c5a8ee00fba92f0589fb8230e0", "path": "uploads/801ea9c5a8ee00fba92f0589fb8230e0", "size": 4243}
จะเห็นได้ว่า ไฟล์ที่เราอัพโหลด เราสามารถรู้ค่า properties ได้ อย่างเช่น
originalname
: ชื่อไฟล์ที่เราอัพโหลดfilename
: ชื่อไฟล์หลังจากที่อัพโหลดmimetype
: ชนิดของไฟล์path
: location ที่เก็บไฟล์หลักจากถูกอัพโหลดsize
: ขนาดของไฟล์
Step 5 - Customize Multer
ต่อมาเรามาปรับแต่ง Multer เพิ่มเติม เนื่องจากตัว default แค่กำหนด folder เท่านั้น ยังไม่ได้ตั้งชื่อไฟล์เลย โดยเราจะใช้ DiskStorage
และกำหนด options ด้วย destination
และ filename
แบบนี้
const storage = multer.diskStorage({ destination: function (req, file, callback) { callback(null, './my-folders') }, filename: function (req, file, callback) { callback(null, file.originalname) }})
const upload = multer({ storage })
destination
- เป็นการบอกว่าให้อัพโหลดไปที่โฟลเดอร์ไหน (เราต้องสร้างโฟลเดอร์เองนะครับ)filename
- function ที่ให้เรากำหนดชื่อไฟล์ได้ โดย return เป็น callback กลับไป ตัวอย่างผมใช้file.originalname
ครับ
กำหนดขนาดไฟล์
เราสามารถกำหนดขนาดไฟล์ด้วยการใช้ limits
เช่น กำหนดให้ไฟล์มีขนาดไม่เกิน 1000 (bytes)
multer({ storage, limits: { fileSize: 1024 * 1024 }})
ทดลองอัพโหลไฟล์ใหม่ จะได้ error (เนื่องจากเรากำหนดขนาดแค่ 1000 bytes หรือ ~1kb เอง)
MulterError: File too large
เปลี่ยนใหม่ ให้เป็น use case จริงๆ เช่น ไฟล์ไม่เกิน 1MB (1M ถ้านับแบบ decimal ก็ 1ล้าน ถ้านับแบบ binary ก็คือ 1024x1024)
{ fileSize: 1024 * 1024 }
รองรับการอัพโหลดหลายๆไฟล์
เราจะใช้ upload.array('images')
เพื่อทำการอัพโหลดหลายๆไฟล์ครับ และเข้าถึงด้วย req.files
เช่น
app.post('/images', upload.array('images'), (req, res) => { res.send(req.files)})
โดยที่ฝั่ง Client (HTML) ก็แค่เพิ่ม attribute multiple
ลงไปแบบนี้
<input type="file" name="images" multiple />
ตัว index.html
แบบที่รองรับทั้ง ไฟล์เดียว และหลายไฟล์
<!doctype html><html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Hello Multer</title> </head> <body> <h1>Hello Multer</h1> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="photo" /> <button type="submit">Upload</button> </form>
<form action="/images" method="post" enctype="multipart/form-data"> <input type="file" name="images" multiple /> <button type="submit">Upload Multiple</button> </form> </body></html>
อื่นๆ
นอกจากนี้ ทางฝั่ง Client เรายังสามารถใช้ FormData เพื่อทำการสร้าง multipart/form-data
ด้วย key value ได้อีกด้วย ประมาณนี้
const form = new FormData()form.append('message', 'Hello')form.append('photo', <FILE>)
เดี๋ยวบทความถัดๆไป จะมีตัวอย่างการทำ File Upload ฝั่ง Client ออกมานะครับ เช่น ใช้ Vanilla JavaScript ธรรมดาๆ ร่วมกับ FileReader API กับ FormData หรือ React.js หรือ Dropzone, Uppy, FilePond เป็นต้นครับ
ปัญหาอื่นๆ
MulterError: Unexpected field
เกิดขึ้น กรณีที่เรากำหนด .single('file')
หรือ .array('files')
และชื่อไม่ตรง input
MulterError: File too large
ไฟล์มีขนาดใหญ่เกิดกว่าที่เรากำหนด
Error: ENOENT: no such file or directory
ไม่มีโฟลเดอร์ที่เรากำหนดให้ multer อัพโหลด (ให้เราสร้างโฟลเดอร์เอง) นอกจากใช้ multer({ dest: 'uploads'})
multer ถึงจะสร้าง folder ให้เราอัตโนมัติ
สรุป
ตัวอย่างนี้ก็เป็นตัวอย่างง่ายๆ ในการทำระบบอัพโหลดไฟล์ด้วย Node.js นะครับ ตัว multer ก็สามารถรองรับทั้งอัพโหลดทีละไฟล์ อัพโหลดหลายๆไฟล์ รวมถึงเรายังสามารถต่อยอด ไปใช้การเก็บข้อมูลลง AWS S3 ได้อีกด้วย ก็หวังว่าบทความนี้จะเป็นประโยชน์ไม่มากก็น้อย ลองเล่น ลองฝึกและลงมือทำกันดูนะครับ
Happy Coding ❤️
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust