Devahoy Logo
PublishedAt

NodeJS

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

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

วิธีการทำระบบไฟล์อัพโหลดด้วยการใช้ Node.js + Express และ Multer ซึ่งบทความนี้เป็นบทความอัพเดท จากที่เคยเขียนไว้ก่อนหน้านี้ 7-8 แล้วครับ ทำระบบอัพโหลดไฟล์ด้วย Node.js ซึ่งตัว Library มันก็มีการอัพเดทไปพอสมควร

Step 1 - Create Project

เริ่มต้นด้วยการสร้างโปรเจ็คขึ้นมาโดยใช้ npm init หรือ yarn init ก็ได้

Terminal window
yarn init -y

จากนั้นก็ทำการติดตั้ง express และ multer

Terminal window
yarn add express multer

Step 2 - Frontend (index.html)

ต่อมาสร้างไฟล์ index.html ขึ้นมา โดยส่วนนี้จะเป็นหน้าเว็บธรรมดา ที่มีแค่การอัพโหลดไฟล์ โดยใช้ <input type="file" />

index.html
1
<!doctype html>
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8" />
5
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
<title>Hello Multer</title>
8
</head>
9
<body>
10
<h1>Hello Multer</h1>
11
<form action="/upload" method="post" enctype="multipart/form-data">
12
<input type="file" name="photo" />
13
<button type="submit" value="Upload" />
14
</form>
15
</body>
16
</html>

โดยที่ตัว form ต้องกำหนด enctype เป็น multipart/form-data

Step 3 - Create Server

สร้าง app.js ขึ้นมา เพื่อเป็น Server ง่ายๆ โดยให้ index ทำการอ่านจากไฟล์ index.html ที่เราสร้างไว้้ และ POST /upload เพื่อไว้รับค่า จากไฟล์ที่เราต้องการอัพโหลด

Test
1
// ไฟล์ app.js
2
const express = require('express')
3
const app = express()
4
5
app.get('/', (req, res) => {
6
res.sendFile(__dirname + '/index.html')
7
})
8
9
app.post('/upload', (req, res) => {
10
res.json(req.body)
11
})
12
13
app.listen(9999, () => console.log('Running on port 9999'))

ทดสอบ โดยการ start server

1
node app.js

ทดสอบเรียก request ไปที่ POST /upload เพื่อดูผลลัพธ์

Terminal window
curl -L -X POST 'http://localhost:9999/upload' \
-H 'Content-Type: application/json' \
-D '{"message": "Hello"}'

หรือทดสอบด้วย Postman

Postman

จะเห็นว่าเราจะไม่ได้ผลลัพธ์อะไร แม้ว่าเราจะให้มัน res.json(req.body) ก็ตาม เพราะเนื่องจาก โดย default ตัว Express ไม่สามารถอ่าน req.body ได้ ถ้าอยากให้อ่านได้ คือใช้ body-parser หรือใช้ตัว express.json() แบบนี้

1
const express = require('express')
2
const app = express()
3
4
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

app.js
1
const multer = require('multer')
2
const upload = multer({ dest: 'uploads' })

โค๊ดด้านบนเป็นการกำหนด multer และให้โฟลเดอร์ที่เราจะเก็บไฟล์คือ uploads สิ่งที่ต้องรู้คือ

  • upload.single : จะอัพโหลดได้ทีละไฟล์
  • ตัว upload.single('photo') ชื่อที่เรากำหนด ต้องเป็นชื่อเดียวกับ form ของเรา <input name="photo" />
app.js
1
const express = require('express')
2
const multer = require('multer')
3
4
const app = express()
5
6
const upload = multer({ dest: 'uploads' })
7
8
app.get('/', (req, res) => {
9
res.sendFile(__dirname + '/index.html')
10
})
11
12
app.post('/upload', upload.single('photo'), (req, res) => {
13
res.send(req.file)
14
})
15
16
app.listen(9999, () => console.log('Running on port 9999'))

ทดลองเปิดหน้าเว็บ http://localhost:9999 จากนั้นเลือกไฟล์แล้วคลิ๊ก Upload ดูครับ จะได้ผลลัพธ์ประมาณนี้

Terminal window
{
"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 แบบนี้

1
const storage = multer.diskStorage({
2
destination: function (req, file, callback) {
3
callback(null, './my-folders')
4
},
5
filename: function (req, file, callback) {
6
callback(null, file.originalname)
7
}
8
})
9
10
const upload = multer({ storage })
  • destination - เป็นการบอกว่าให้อัพโหลดไปที่โฟลเดอร์ไหน (เราต้องสร้างโฟลเดอร์เองนะครับ)
  • filename - function ที่ให้เรากำหนดชื่อไฟล์ได้ โดย return เป็น callback กลับไป ตัวอย่างผมใช้ file.originalname ครับ

กำหนดขนาดไฟล์

เราสามารถกำหนดขนาดไฟล์ด้วยการใช้ limits เช่น กำหนดให้ไฟล์มีขนาดไม่เกิน 1000 (bytes)

1
multer({ storage, limits: { fileSize: 1024 * 1024 }})

ทดลองอัพโหลไฟล์ใหม่ จะได้ error (เนื่องจากเรากำหนดขนาดแค่ 1000 bytes หรือ ~1kb เอง)

1
MulterError: File too large

เปลี่ยนใหม่ ให้เป็น use case จริงๆ เช่น ไฟล์ไม่เกิน 1MB (1M ถ้านับแบบ decimal ก็ 1ล้าน ถ้านับแบบ binary ก็คือ 1024x1024)

1
{ fileSize: 1024 * 1024 }

รองรับการอัพโหลดหลายๆไฟล์

เราจะใช้ upload.array('images') เพื่อทำการอัพโหลดหลายๆไฟล์ครับ และเข้าถึงด้วย req.files เช่น

1
app.post('/images', upload.array('images'), (req, res) => {
2
res.send(req.files)
3
})

โดยที่ฝั่ง Client (HTML) ก็แค่เพิ่ม attribute multiple ลงไปแบบนี้

1
<input type="file" name="images" multiple />

ตัว index.html แบบที่รองรับทั้ง ไฟล์เดียว และหลายไฟล์

1
<!doctype html>
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8" />
5
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
<title>Hello Multer</title>
8
</head>
9
<body>
10
<h1>Hello Multer</h1>
11
<form action="/upload" method="post" enctype="multipart/form-data">
12
<input type="file" name="photo" />
13
<button type="submit">Upload</button>
14
</form>
15
16
<form action="/images" method="post" enctype="multipart/form-data">
17
<input type="file" name="images" multiple />
18
<button type="submit">Upload Multiple</button>
19
</form>
20
</body>
21
</html>

HTML File input

อื่นๆ

นอกจากนี้ ทางฝั่ง Client เรายังสามารถใช้ FormData เพื่อทำการสร้าง multipart/form-data ด้วย key value ได้อีกด้วย ประมาณนี้

1
const form = new FormData()
2
form.append('message', 'Hello')
3
form.append('photo', <FILE>)

เดี๋ยวบทความถัดๆไป จะมีตัวอย่างการทำ File Upload ฝั่ง Client ออกมานะครับ เช่น ใช้ Vanilla JavaScript ธรรมดาๆ ร่วมกับ FileReader API กับ FormData หรือ React.js หรือ Dropzone, Uppy, FilePond เป็นต้นครับ

ปัญหาอื่นๆ

Terminal window
MulterError: Unexpected field

เกิดขึ้น กรณีที่เรากำหนด .single('file') หรือ .array('files') และชื่อไม่ตรง input

Terminal window
MulterError: File too large

ไฟล์มีขนาดใหญ่เกิดกว่าที่เรากำหนด

Terminal window
Error: ENOENT: no such file or directory

ไม่มีโฟลเดอร์ที่เรากำหนดให้ multer อัพโหลด (ให้เราสร้างโฟลเดอร์เอง) นอกจากใช้ multer({ dest: 'uploads'}) multer ถึงจะสร้าง folder ให้เราอัตโนมัติ

สรุป

ตัวอย่างนี้ก็เป็นตัวอย่างง่ายๆ ในการทำระบบอัพโหลดไฟล์ด้วย Node.js นะครับ ตัว multer ก็สามารถรองรับทั้งอัพโหลดทีละไฟล์ อัพโหลดหลายๆไฟล์ รวมถึงเรายังสามารถต่อยอด ไปใช้การเก็บข้อมูลลง AWS S3 ได้อีกด้วย ก็หวังว่าบทความนี้จะเป็นประโยชน์ไม่มากก็น้อย ลองเล่น ลองฝึกและลงมือทำกันดูนะครับ

Happy Coding ❤️

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts