博客 / 詳情

返回

Vue + Node.js 搭建「文件上傳」管理後台

本文完整版《Vue + Node.js 搭建「文件上傳」管理後台》

本教程手把手帶領大家搭建一套通過 Vue + Node.js 上傳文件的後台系統,只要你跟隨本教程一步步走,一定能很好的理解整個前後端上傳文件的代碼邏輯。前端我們使用 Vue + Axios + Multipart 來搭建前端上傳文件應用,後端我們使用 Node.js + Express + Multer 來搭建後端上傳文件處理應用。

當然,本教程還會教給大家如何寫一個可以限制上傳文件大小、有百分比進度條、可報錯、可顯示服務器上文件列表、可點擊下載文件的前端操作界面。

最後完成的上傳文件工具後台如下圖,跟隨本教學習,你也可以搭出來。

vue 搭建文件上傳管理工具

如果你對前端不是很熟悉,不想寫前端,推薦使用卡拉雲搭建後台管理系統,卡拉雲是新一代低代碼開發工具,不用懂任何前端技術,僅靠鼠標拖拽,即可快速搭建包括「上傳文件」在內的任何後台管理工具。立即試用卡拉雲 1 分鐘搭建「文件上傳」工具。詳情見本文文末。

Vue + Node.js「上傳文件」前後端項目結構

kalacloud-upload-file-vue-nodejs

Vue 前端部分

  • UploadFilesService.js:這個腳本調用通過 Axios 保存文件和獲取文件的方法
  • UploadFiles.vue:這個組件包含所有上傳文件相關的信息和操作
  • App.vue:把我們的組件導入到 Vue 起始頁
  • index.html:用於導入 Bootstrap
  • http-common.js:配置並初始化 Axios
  • vue.config.js:配置 APP 端口

Node.js 後端部分

  • resources/static/assets/uploads:用於存儲上傳的文件
  • middleware/upload.js:初始化 Multer 引擎並定義中間件
  • file.controller.js:配置 Rest API
  • routes/index.js:路由,定義前端請求後端如何執行
  • server.js:運行Node.js Express 應用

✦ 前端部分 - 上傳文件 Vue + Axios + Multipart

配置 Vue 環境

使用 npm 安裝 Vue 腳手架 vue-cli

npm install -g @vue/cli

vue-setup

然後我們創建一個 Vue 項目 kalacloud-vue-multiple-files-upload

vue create kalacloud-vue-multiple-files-upload

安裝完成後,cd 進入 kalacloud-vue-multiple-files-upload 目錄,接下來所有操作都在這個目錄之下。

安裝 Axios:

npm install axios

我們先跑一下 Vue ,這是 vue 的默認狀態

npm run serve

vue-run

我們可以看到瀏覽器裏 Vue 已經在 localhost:8080 跑起來了。

擴展閲讀:《Vue 搭建帶預覽的「上傳圖片」管理後台》

導入 Bootstrap 到項目中

打開 index.html 把以下代碼添加到<head> 中:

文件位置:public/index.html

<!DOCTYPE html>
<html lang="en"><head>
...
<link type="text/css" rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" />
</head>
  ...
</html>

初始化 Axios HTTP 客户端

在 src 文件夾下,創建 http-common.js 文件,如下所示:

文件位置:src/http-common.js

import axios from "axios";
export default axios.create({
  baseURL: "http://localhost:8080",
  headers: {
    "Content-type": "application/json"
  }
});

這裏的 baseURL 是你上傳文件的後端服務器 REST API 地址,請根據實際情況修改。本教程後文,教你搭建上傳文件的後端部分,請繼續閲讀。

創建「上傳文件」功能

我們來寫一個 JS 腳本,這個腳本調用 Axios 發送 HTTP API 請求,與後端服務器通訊。

這個腳本包含 2 個功能

  • upload(file): POST 數據到後端,再加一個上傳進度的回調,可以放個上傳進度條。
  • getFiles(): 用於獲取服務器上傳文件夾中的文件列表

文件位置:src/services/UploadFilesService.js

import http from "../http-common";
class UploadFilesService {
  upload(file, onUploadProgress) {
    let formData = new FormData();
    formData.append("file", file);
    return http.post("/upload", formData, {
      headers: {
        "Content-Type": "multipart/form-data"
      },
      onUploadProgress});
  }
  getFiles() {
    return http.get("/files");
  }
}
export default new UploadFilesService();
  • 首先導入我們剛剛寫好的 Axios HTTP 配置文件 http-common.js
  • FormData 是一種可將數據編譯成鍵值對的數據結構
  • Axios的進度條事件,onUploadProgress 是用來監測上傳進度,顯示進度信息
  • 最後我們調用 Axios 提供的 post()&get() 來向後端 API 發送 POST & GET 請求

擴展閲讀:《Vue Router 手把手教你搭 Vue3 路由》

創建一個 Vue 多文件上傳組件

接下來,我們來寫一個 Vue 上傳組件,這個組件要包含上傳文件的所有基本功能,比如 上傳按鈕、進度條、提示信息、基本 UI 等。

首先,創建一個 Vue 組件模版(UploadFiles.vue)然後把剛剛寫好的配置文件(UploadFilesService.js)導入進去。

文件位置:src/components/UploadFiles.vue

<template>
</template>
<script>
import UploadService from "../services/UploadFilesService";
export default {
  name: "upload-files",
  data() {
    return {
    };
  },
  methods: {}
};
</script>

然後在這裏定義 data() 變量

export default {
  name: "upload-files",
  data() {
    return {
      selectedFiles: undefined,
      progressInfos: [],
      message: "",
      fileInfos: [],
    };
  },
};

接下來,我們定義 methods 讓 selectFiles() 從 <input type="file" >中獲取選定的文件。

export default {
  name: "upload-files",
  ...
  methods: {
    selectFile() {
      this.progressInfos = [];
      this.selectedFiles = event.target.files;
    }
  }
};

selectedFiles 用來訪問當前選定的文件,每個文件都有一個對應的進度條(百分比&文件名)以及被 progressInfos 索引。

export default {
  name: "upload-files",
  ...
  methods: {
    ...
    uploadFiles() {
      this.message = "";
      for (let i = 0; i < this.selectedFiles.length; i++) {
        this.upload(i, this.selectedFiles[i]);
      }
    }
  }
};

我們通過回調 UploadFilesService.upload() 來獲得上傳信息

export default {
  name: "upload-files",
  ...
  methods: {
    ...
    upload(idx, file) {
      this.progressInfos[idx] = { percentage: 0, fileName: file.name };
      UploadService.upload(file, (event) => {
        this.progressInfos[idx].percentage = Math.round(100 * event.loaded / event.total);
      })
        .then((response) => {
          let prevMessage = this.message ? this.message + "\n" : "";
          this.message = prevMessage + response.data.message;
          return UploadService.getFiles();
        })
        .then((files) => {
          this.fileInfos = files.data;
        })
        .catch(() => {
          this.progressInfos[idx].percentage = 0;
          this.message = "Could not upload the file:" + file.name;
        });
    }
  }
};
  • 文件上傳進度我們可以根據event.loadedevent.total 來計算
  • 如果傳輸完成,我們調用UploadFilesService.getFiles()來獲取文件信息,並將結果更新到 fileInfos 裏,狀態是一個數組 {name, url}

我們還需要在mounted() 中添加調用。

export default {
  name: "upload-files",
  ...
  mounted() {
    UploadService.getFiles().then((response) => {
      this.fileInfos = response.data;
    });
  }
};

現在我們實現上傳文件 UI 的 HTML 模板。將以下內容添加到<template>

<template>
  <div>
    <div v-if="progressInfos">
      <div class="mb-2"
        v-for="(progressInfo, index) in progressInfos"
        :key="index"
      >
        <span>{{progressInfo.fileName}}</span>
        <div class="progress">
          <div class="progress-bar progress-bar-info"
            role="progressbar"
            :aria-valuenow="progressInfo.percentage"
            aria-valuemin="0"
            aria-valuemax="100"
            :style="{ width: progressInfo.percentage + '%' }"
          >
            {{progressInfo.percentage}}%
          </div>
        </div>
      </div>
    </div>
    <label class="btn btn-default">
      <input type="file" multiple @change="selectFile" />
    </label>
    <button class="btn btn-success"
      :disabled="!selectedFiles"
      @click="uploadFiles"
    >
      上傳
    </button>
    <div v-if="message" class="alert alert-light" role="alert">
      <ul>
        <li v-for="(ms, i) in message.split('\n')" :key="i">
          {{ ms }}
        </li>
      </ul>
    </div>
    <div class="card">
      <div class="card-header"> 文件列表 </div>
      <ul class="list-group list-group-flush">
        <li class="list-group-item"
          v-for="(file, index) in fileInfos"
          :key="index"
        >
          <a :href="file.url">{{ file.name }}</a>
        </li>
      </ul>
    </div>
  </div>
</template>

在上面的代碼中,我們使用 Bootstrap 進度條,這裏不展開講了,更多細節可查看 Bootstrap 文檔。

在 App.vue 中添加「文件上傳」組件

打開 App.vue ,在代碼中導入 UploadFiles 組件。

<template>
  <div id="app">
    <div class="container" style="width:600px">
      <div style="margin: 20px">
<h2>使用 Vue 搭建文件上傳 Demo</h2>
        <h5><a href="http://kalacloud.com">卡拉雲</a> - 低代碼開發工具,1 秒搭建上傳後台</h5>
                <a>使用卡拉雲無需懂任何前端技術,僅需拖拽即可搭建屬於你的後台管理系統</a>

      </div>
      <upload-files></upload-files>
    </div>
  </div>
</template>
<script>
import UploadFiles from "./components/UploadFiles";
export default {
  name: "App",
  components: {
    UploadFiles
  }
};
</script>

給 Vue 上傳文件服務配置訪問端口

文件位置:src/vue.config.js

module.exports = {
  devServer: {
    port: 8081
  }
}

運行 Vue 前端部分

到這裏,我們已經把 Vue 上傳文件的前端部分寫完,運行起來看看效果吧。

在 kalacloud-vue-multiple-files-upload 根目錄跑一下:

npm run serve

在瀏覽器打開 http://localhost:8081/ 可以看到前端部分已經完美顯示。

vue-uploads-files

文件選擇器、上傳按鈕、文件列表都已經可以顯示出來了,但還無法上傳。這是因為後端部分還沒有跑起來,接下來,我帶領大家手把手搭建上傳文件的後端部分。

擴展閲讀:《5 款最棒的 Vue UI 移動端組件庫 - 特別針對國內使用場景推薦》

Vue 前端「上傳文件」源碼

你可以在我的 github 上下載到完整的 Vue 上傳文件 Demo。

當然你也可以不用這麼費勁搭建前端做上傳文件功能,直接使用卡拉雲,無需懂前後端,簡單拖拽即可生成一套屬於你自己的後台管理工具。

✦ 後端部分 - 上傳文件 Node.js + Express + Multer

前文我們介紹瞭如何使用 Vue 搭建上傳文件管理工具的前端部分,接下來我教大家使用 Node.js + Express + Multer 來搭建一套上傳文件的後端 Rest API,提供給 Vue 前端使用,從而實現 Vue 選擇文件 + Axios 調用後端 API HTTP 通訊,最後把文件上傳到服務器指定目錄。

接下來,大家一起跟隨本教程創建一套 Node.js 上傳文件 Rest API,它的功能包括:

  • 將 Vue 前端選中的文件上傳到服務器的靜態文件夾中
  • 限制上傳文件大小,最大 2MB
  • GET 服務器中存儲文件的 URL ,可用於下載
  • GET 文件信息列表(文件名 + URL)

這是存儲所有上傳文件的靜態文件夾:

node-uploads

如果我們 GET 文件列表,Node.js Rest API 會返回:

postman-get

GET /files ,API 返回 文件名 + URL

我們構建的 Node.js Rest API 包含這三個功能:

  • POST /upload 上傳一個文件
  • GET /files 獲取文件列表(文件名+URL)
  • GET /files/[filename] 下載指定文件

擴展閲讀:《頂級好用的 5 款 Vue table 表格組件測評與推薦》

配置 Node.js 開發環境

在根目錄新建 Node.js 的後端文件夾 kalacloud-express-file-upload,接下來所有操作都在這個文件夾中進行。

mkdir kalacloud-express-file-upload
cd kalacloud-express-file-upload

在 kalacloud-express-file-upload 文件夾根目錄安裝 Express、Multer、CORS 這三個模塊:

npm install express multer cors

package.json 文件:

{
  "name": "kalacloud-express-file-upload",
  "version": "1.0.0",
  "description": "Node.js Express Rest APis to Upload/Download Files",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "node js",
    "upload",
    "download",
    "file",
    "multipart",
    "rest api",
    "express"
  ],
  "author": "bezkoder",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "multer": "^1.4.2"
  }
}

配置文件上傳中間件 Multer

我們使用 Multer 中間件來處理多文件上傳,更多 Multer 細節請閲讀它的開發文檔

文件位置:src/middleware/upload.js

const util = require("util");
const multer = require("multer");
const maxSize = 2 * 1024 * 1024;
let storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, __basedir + "/resources/static/assets/uploads/");
  },
  filename: (req, file, cb) => {
    console.log(file.originalname);
    cb(null, file.originalname);
  },
});
let uploadFile = multer({
  storage: storage,
  limits: { fileSize: maxSize },
}).single("file");
let uploadFileMiddleware = util.promisify(uploadFile);
module.exports = uploadFileMiddleware;

上面的代碼我們做了這麼幾件事:

  • 導入 multer 模塊。
  • 配置 multer 為磁盤存儲引擎。
  • destination:指向用於存儲上傳文件的文件夾。
  • filename:上傳文件上傳後的文件名。

擴展閲讀:《最好用的 7 款 Vue admin 後台管理框架測評》

使用 Multer 限制文件大小

我們可以使用 Multer API 來限制上傳文件大小,添加 limits: { fileSize: maxSize } 以限制文件大小。

let storage = multer.diskStorage(...);
const maxSize = 2 * 1024 * 1024;
let uploadFile = multer({
  storage: storage,
  limits: { fileSize: maxSize }
}).single("file");

創建文件上傳 / 下載控制器

在 controller 文件夾中創建 file.controller.js

上傳文件:我們使用 upload() 函數

  • 使用中間件功能上傳文件
  • 上傳文件錯誤信息(在 Multer 中間件函數中)
  • 返回信息

下載文件:

  • 使用 getListFiles() 讀取服務器上傳文件夾中的所有文件,包含文件名和 URL
  • 使用 download() 接收文件名作為輸入參數,然後使用 Express res.downloa() 以附件形式傳輸 URL(目錄+文件名)

文件位置:src/controller/file.controller.js

const uploadFile = require("../middleware/upload");
const upload = async (req, res) => {
  try {
    await uploadFile(req, res);
    if (req.file == undefined) {
      return res.status(400).send({ message: "請選擇要上傳的文件" });
    }
    res.status(200).send({
      message: "文件上傳成功: " + req.file.originalname,
    });
  } catch (err) {
    res.status(500).send({
      message: `無法上傳文件: ${req.file.originalname}. ${err}`,
    });
  }
};
const getListFiles = (req, res) => {
  const directoryPath = __basedir + "/resources/static/assets/uploads/";
  fs.readdir(directoryPath, function (err, files) {
    if (err) {
      res.status(500).send({
        message: "沒有找到文件。",
      });
    }
    let fileInfos = [];
    files.forEach((file) => {
      fileInfos.push({
        name: file,
        url: baseUrl + file,
      });
    });
    res.status(200).send(fileInfos);
  });
};
const download = (req, res) => {
  const fileName = req.params.name;
  const directoryPath = __basedir + "/resources/static/assets/uploads/";
  res.download(directoryPath + fileName, fileName, (err) => {
    if (err) {
      res.status(500).send({
        message: "無法獲取文件。" + err,
      });
    }
  });
};
module.exports = {
  upload,
  getListFiles,
  download,
};
  • 我們首先調用中間件函數 uploadFile()
  • 如果 HTTP 請求不包含文件,返回 400 錯誤信息
  • 如果出現獲取錯誤,返回 500 錯誤信息

如果用户上傳文件大小超限的文件應該怎麼處理?

擴展閲讀:《手把手教你Vue3+Node.js+Expres+MySQL環境搭建》

使用 Multer 處理文件大小超限錯誤

我們可以通過 catch() 來檢查文件超限錯誤(LIMIT_FILE_SIZE

文件位置:src/controller/file.controller.js

 const upload = async (req, res) => {
  try {
    await uploadFile(req, res);
    ...
  } catch (err) {
    if (err.code == "LIMIT_FILE_SIZE") {
      return res.status(500).send({
        message: "文件大小不能超過 2MB",
      });
    }
    res.status(500).send({
      message: `不能上傳文件: ${req.file.originalname}. ${err}`,
    });
  }
};

設置後端 Rest API 上傳文件的路徑

當 Vue 前端通過 Axios 發送 HTTP 請求時,我們需要通過路由來確定服務器應該如何響應

我們來設置三種常用到的上傳文件所需功能

  • POST /uploadupload()
  • GET /filesgetListFiles()
  • GET /files/[fileName]download()

我們在 routes 文件夾中創建 index.js 文件:

文件位置:src/routes/index.js

const express = require("express");
const router = express.Router();
const controller = require("../controller/file.controller");
let routes = (app) => {
  router.post("/upload", controller.upload);
  router.get("/files", controller.getListFiles);
  router.get("/files/:name", controller.download);
  app.use(router);
};
module.exports = routes;

上面這段代碼調用了我們前文寫的控制器 file.controller.js

擴展閲讀:《最好的 5 款翻譯 API 接口對比測評》

創建 Express 服務

最後一步,創建 Express 服務,在根目錄新建一個 server.js 文件

文件位置:kalacloud-express-file-upload/server.js

const cors = require("cors");
const express = require("express");
const app = express();
global.__basedir = __dirname;
var corsOptions = {
  origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
const initRoutes = require("./src/routes");
app.use(express.urlencoded({ extended: true }));
initRoutes(app);
let port = 8080;
app.listen(port, () => {
  console.log(`Running at localhost:${port}`);
});

導入 express 和 cors 模塊:

  • 創建 Express 應用,用於構建 Rest API,然後添加cors中間件。
  • 設置 http://localhost:8081 為 origin ,這裏允許前端傳入

擴展閲讀:《如何使 Vue ECharts 柱狀圖中,每個柱子顏色各不同(隨機或指定顏色)》

運行後端並測試

首先,在 kalacloud-express-file-upload 根目錄執行

node server.js

把後端服務啓動起來。然後我們使用 Postman 來發送 HTTP 請求,看看後端是否運行正常。

➜  kalacloud-express-file-upload node server.js
Running at localhost:8080

接着我們使用 Postman 來測試一下,我們剛剛搭建的後端服務器是否能正常運行

向後端服務器發 POST 請求上傳文件

postman-post-update

上傳大於最大限制 (2MB) 的文件,500 報錯。

postman-post-file-sitz-cannot-be

GET 檢索文件信息列表:

postman-get

我們可以使用返回的文件 URL 下載這些文件,例如: http://localhost:8080/files/kalacloud-logo.png

擴展閲讀:《Video.js 使用教程 - 手把手教你基於 Vue 搭建 HTML 5 視頻播放器》

Vue + Node.js 上傳文件前後端一起運行

在 kalacloud-vue-multiple-files-upload 文件夾根目錄運行前端 Vue

npm run serve

在 kalacloud-express-file-upload 文件夾根目錄運行後端 Node.js

node server.js

然後打開瀏覽器輸入前端訪問網址:

kalacloud-vue-node-upload-file

到這裏整個前後端「上傳文件」管理工具就搭建完成了。

Node.js 後端「上傳文件」源碼

你可以在我的 github 上下載到完整的 Node.js 後端「上傳文件」源碼。

如果你還沒搞懂,也不用着急,直接使用卡拉雲,無需懂任何前後端技術,僅需簡單的鼠標拖拽即可快速生成包括「上傳文件」管理在內的任何後台管理工具。立即試用卡拉雲。

「上傳文件」前後端搭建總結

本教程手把手教大家搭建 Vue 前端 + Node.js 後端 的「文件上傳」管理工具,如果你一步步跟着走,一定已經把 Demo 跑起來了。但如果你會使用最新的低代碼開發工具「卡拉雲」,完全不需要這麼繁瑣,僅需 1 分鐘,就能搭建起屬於自己的「文件上傳」管理工具。

kalacloud-uploads

註冊開通卡拉雲,從側邊工具欄直接拖拽組件到頁面,生成上傳組件和文件管理工具。1 分鐘搞定「文件上傳」管理工具。

擴展閲讀:

  • Video.js 使用教程 - 手把手教你基於 Vue 搭建 HTML 5 視頻播放器
  • 最好的 6 個免費天氣 API 接口對比測評
  • 12 款最棒 Vue 開源 UI 庫測評 - 特別針對國內使用場景推薦
  • 最棒的 7 個 Laravel admin 後台管理系統推薦
  • Retool 是什麼,怎麼樣? —— Retool 低代碼工具測評
  • 最好用的 5 個 Vue select 單選多選下拉組件
  • 最好用的 7 款 Vue admin 後台管理框架測評
user avatar lvtu 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.