iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0

https://ithelp.ithome.com.tw/upload/images/20230929/20136558XnhgQYNVVp.jpg
今天要來開發圖片上傳的API,我們在使用者個人資料和文章封面都會使用到這支API的功能。

大綱

  1. 圖片存放位置比較(伺服器 vs.資料庫)
  2. 圖片上傳API開發

1.圖片存放位置比較(伺服器 vs.資料庫)

首先想先來探討,圖片上傳的存放位置要放在哪比較好,
圖片可以存放到伺服器端,也可以存放到資料庫,接下來先來分兩者的優缺點

1. 存放在伺服器端的檔案系統:

✔️優點:

  • 效能: 讀取伺服器上的檔案通常比從資料庫中讀取大型二進位(binary)檔案更快。
  • 簡單性: 在伺服器上的檔案系統中管理檔案通常比在資料庫中更簡單。此外,對於備份、遷移和存儲大量資料的工具,伺服器檔案系統通常比資料庫更具優勢。
  • 直接存取: 如果你需要直接訪問或修改這些檔案,檔案系統比資料庫更加方便。

❌缺點:

  • 擴展性: 如果你的應用軟體是分散式的或運行在多個伺服器上,同步和管理伺服器之間的檔案可能會變得複雜。
  • 冗餘性: 檔案可能需要透過某種方式進行備份,以確保不會因單點故障(Single Point of Failure)而遺失。

2. 存放在資料庫中:

✔️優點:

  • 組織性: 檔案和其相關的元資料(metadata)都儲存在同一位置,這可以使查詢和組織變得更加方便。
  • 備份和復原: 如果所有資料都存放在資料庫中,那麼整個系統的備份和恢復會更加簡單。
  • 擴展性: 對於分散式系統或多伺服器架構,儲存在資料庫中的檔案可以更容易地與其他資料一起複製和分配。

❌缺點:

  • 效能: 將大型檔案儲存在資料庫中可能會影響效能,尤其是當資料庫需要頻繁讀取或寫入大量資料時。
  • 資料庫大小: 儲存大量的大檔案會迅速增加資料庫的大小,造成需要更多的儲存空間和資源來維護。
  • 複雜性: 依賴資料庫來儲存和管理檔案可能會增加應用程式的複雜性。

綜合以上分析,因為我們的系統需要頻繁讀取和使用圖片,而且這些圖片的大小相對較大,所以我們選擇存放在伺服器


2.圖片上傳API開發

首先先安裝我們此次要用到的套件multer

npm install multer

接著在專案下建立uploads這個資料夾,用來存放上傳的圖片
https://ithelp.ithome.com.tw/upload/images/20230929/20136558bIEBsp9XNh.jpg

config資料夾底下default.json新增 "fileMaxSize": "2000000"

{
    "mongoURI": <資料庫連結>,
    "jwtSecret": "secrettoken",
    "fileMaxSize": "2000000"
}

controllers資料夾底下新增images-controller.js

//image-controller.js

const HttpError = require('../models/http-error');
const config = require('config');

//圖片上傳
exports.uploadImage = (req, res, next) => {
    if (!req.file) {
        const error = new HttpError('沒有檔案被上傳', 400);
        return next(error);
    }
    const imageUrl = `http://localhost:5000/uploads/${req.file.filename}`;
    res.json({ success: true, data: { url: imageUrl } });
};

//圖片檔案格式和大小判斷
exports.uploadErrorHandler = (err, req, res, next) => {
    if (err.code === "LIMIT_FILE_TYPES") {
        res.status(422).json({ error: "只支援jpg、jpeg、png" });
        return;
    }
    if (err.code === "LIMIT_FILE_SIZE") {
        res.status(422).json({ error: `檔案過大,最大為 ${config.get('fileMaxSize')} / 1000000}MB` });
        return;
    }
    next(err);
};

routes資料夾底下新增image-routes.js

//image-routes.js

const express = require('express');
const multer = require('multer'); //引入安裝的套件
const path = require('path');
const auth = require('../../middleware/auth');
const router = express.Router();
const imageControllers = require("../../controllers/images-controller");
const config = require('config');


//判斷檔案類型是否支援
const fileFilter = (req, file, cb) => {
  const allowedTypes = ["image/jpeg", "image/jpg", "image/png"];
  if (!allowedTypes.includes(file.mimetype)) {
    const error = new Error("不支援的檔案類型(只支援jpg、jpeg、png)");
    error.code = "LIMIT_FILE_TYPES";
    return cb(error, false);
  }
  cb(null, true);
};

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/') // This directory should exist in your server root.
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + path.extname(file.originalname)); // Append the date to prevent overwriting.
  }
});

// Multer setup
const upload = multer({ 
  storage: storage,
  fileFilter: fileFilter,
  limits:{
    fileSize: config.get("fileMaxSize")
  } 
});


router.post('/upload', auth, upload.single('image'), imageControllers.uploadImage, imageControllers.uploadErrorHandler);

module.exports = router;

最後到server.js裡面引入image routes,並設定express

//server.js
const express = require('express');
const connectDB = require('./config/db');
const app = express();
const bodyParser = require('body-parser');
const users = require('./routes/api/users-route');
const auth = require('./routes/api/auth-route');
const posts = require('./routes/api/posts-route');
const images = require('./routes/api/images-route');

connectDB();

app.use(bodyParser.json());

/**
 * 告訴 Express 提供在 uploads 目錄中的靜態檔案
 * 當瀏覽器或任何客戶端向 /uploads 發起請求時,它實際上是在請求伺服器的 uploads 目錄中的檔案。
 **/
app.use('/uploads', express.static('uploads'));


(略...)
app.use('/api/users', users);
app.use('/api/auth', auth);
app.use('/api/posts', posts);
app.use('/api/images', images);


Postman 測試

圖片上傳
https://ithelp.ithome.com.tw/upload/images/20230929/20136558oWo6cXgClY.jpg

/images/emoticon/emoticon09.gif
⚠️這邊有一個我在開發時踩到的坑,當時我都是使用網頁版的postman在進行測試,但當我要來網頁版的postman測試圖片上傳時會一直出現選不到檔案的問題,查了一下原來要使用桌面版的postman才可以正常抓取電腦上的檔案
- 關於此議題更詳細的解釋

參考資料


上一篇
[Day13] 其他取得資料API開發
下一篇
[Day15]後端開發總結
系列文
初探全端之旅: 以MERN技術建立個人部落格31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言