 
 วิธีการทำระบบไฟล์อัพโหลดด้วยการใช้ 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 multerStep 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 
![[Workshop] ทำ Chat Application ด้วย Express + Socket.io และ React.js](/_vercel/image?url=%2Fimages%2F2023%2F10%2Fapp-chat-react.png&w=828&q=80) 
 ![[Workshop] ทำ Chat Application ด้วย Node.js, Express และ Socket.io](/_vercel/image?url=%2Fimages%2F2023%2F10%2Fchat-app-nodejs-socketio.png&w=828&q=80) 
 