您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“怎么使用Reactjs+Nodejs+Mongodb實現(xiàn)文件上傳功能”,內容詳細,步驟清晰,細節(jié)處理妥當,希望這篇“怎么使用Reactjs+Nodejs+Mongodb實現(xiàn)文件上傳功能”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
├── README.md ├── package-lock.json └── node_modules └── ... ├── package.json ├── public │ └── index.html └── src ├── App.css ├── App.js ├── components │ └── UploadFiles.js ├── http-common.js ├── index.js └── services └── UploadFilesService.js
App.js
: 把我們的組件導入到 React 的起始頁
components/UploadFiles.js
: 文件上傳組件
http-common.js
: 使用 HTTP 基礎 Url 和標頭初始化 Axios。
我們在.env中為我們的應用程序配置端口
services/UploadFilesService.js
: 這個文件中的函數(shù)用于文件上傳和獲取數(shù)據庫中文件數(shù)據
├── README.md ├── package.json ├── pnpm-lock.yaml └── node_modules └── ... └── src ├── config │ └── db.js ├── controllers │ └── flileUploadController.js ├── middleware │ └── upload.js ├── routes │ └── index.js └── server.js
src/db.js
包括 MongoDB 和 Multer 的配置(url、數(shù)據庫、文件存儲桶)。
middleware/upload.js
:初始化 Multer GridFs 存儲引擎(包括 MongoDB)并定義中間件函數(shù)。
controllers/flileUploadController.js
:配置 Rest API
routes/index.js
:路由,定義前端請求后端如何執(zhí)行
server.js
:Node.js入口文件
這里我們使用 pnpm vite 創(chuàng)建一個 React 項目
npx create-react-app react-multiple-files-upload
項目創(chuàng)建完成后,cd 進入項目,安裝項目運行需要的依賴包和 Axios 終端分別依次如下命令
pnpm install pnpm install axios
執(zhí)行完成我們啟動項目 pnpm start
可以看到控制臺中已經輸出了信息,在瀏覽器地址欄中輸入控制臺輸出的地址,項目已經跑起來了
運行如下命令
npm install bootstrap
bootstrap
安裝完成后,我們打開 src/App.js
文件, 添加如下代碼
import React from "react"; import "./App.css"; import "bootstrap/dist/css/bootstrap.min.css"; function App() { return ( <div className="container"> ... </div> ); } export default App;
在 src
目錄下 我們新建 http-common.js
文件,并添加如下代碼
import axios from "axios"; export default axios.create({ baseURL: "http://localhost:8080", headers: { "Content-type": "application/json" } });
這里 baseURL 的地址是我們后端服務器的 REST API 地址,要根據個人實際情況進行修改。本教程后文,教你搭建上傳文件的后端部分,請繼續(xù)閱讀。
src/services/UploadFilesService.js
,這個文件主要的作用就是和后端項目通訊,以進行文件的上傳和文件列表數(shù)據的獲取等
在文件中我們加入如下內容
import http from "../http-common"; const upload = (file, onUploadProgress) => { let formData = new FormData(); formData.append("file", file); return http.post("/upload", formData, { headers: { "Content-Type": "multipart/form-data", }, onUploadProgress, }); }; const getFiles = () => { return http.get("/files"); }; const FileUploadService = { upload, getFiles, }; export default FileUploadService;
首先導入我們之前寫好的 Axios HTTP 配置文件 http-common.js,并定義一個對象,在對象中添加兩個屬性函數(shù),作用如下
upload
:函數(shù)以 POST 的方式將數(shù)據提交到后端,接收兩個參數(shù) file
和 onUploadProgress
file
上傳的文件,以 FormData
的形式上傳
onUploadProgress
文件上傳進度條事件,監(jiān)測進度條信息
getFiles
: 函數(shù)用于獲取存儲在 Mongodb 數(shù)據庫中的數(shù)據
最后將這個對象導出去
接下來我們來創(chuàng)建文件上傳組件,首先組件要滿足功能有文件上傳,上傳進度條信息展示,文件預覽,提示信息,文件下載等功能
這里我們使用 React Hooks 和 useState 來創(chuàng)建文件上傳組件,創(chuàng)建文件 src/components/UploadFiles
,添加如下代碼
import React, { useState, useEffect, useRef } from "react"; import UploadService from "../services/UploadFilesService"; const UploadFiles = () => { return ( ); }; export default UploadFiles;
然后我們使用 React Hooks 定義狀態(tài)
const UploadFiles = () => { const [selectedFiles, setSelectedFiles] = useState(undefined); const [progressInfos, setProgressInfos] = useState({ val: [] }); const [message, setMessage] = useState([]); const [fileInfos, setFileInfos] = useState([]); const progressInfosRef = useRef(null) }
狀態(tài)定義好后,我們在添加一個獲取文件的方法 selectFiles()
const UploadFiles = () => { ... const selectFiles = (event) => { setSelectedFiles(event.target.files); setProgressInfos({ val: [] }); }; ... }
selectedFiles
用來存儲當前選定的文件,每個文件都有一個相應的進度信息如文件名和進度信息等,我們將這些信息存儲在 fileInfos
中。
const UploadFiles = () => { ... const uploadFiles = () => { const files = Array.from(selectedFiles); let _progressInfos = files.map(file => ({ percentage: 0, fileName: file.name })); progressInfosRef.current = { val: _progressInfos, } const uploadPromises = files.map((file, i) => upload(i, file)); Promise.all(uploadPromises) .then(() => UploadService.getFiles()) .then((files) => { setFileInfos(files.data); }); setMessage([]); }; ... }
我們上傳多個文件的時候會將文件信息存儲在 selectedFiles
, 在上面的代碼中 我們使用 Array.from
方法將可迭代數(shù)據轉換數(shù)組形式的數(shù)據,接著使用 map
方法將文件的進度信息,名稱信息存儲到 _progressInfos
中
接著我們使用 map
方法調用 files
數(shù)組中的每一項,使 files
中的每一項都經過 upload
函數(shù)的處理,在 upload
函數(shù)中我們會返回上傳文件請求函數(shù) UploadService.upload
的 Promise
狀態(tài)
所以 uploadPromises
中存儲的就是處于 Promise 狀態(tài)的上傳文件函數(shù),接著我們使用 Promise.all
同時發(fā)送多個文件上傳請求,在所有文件都上傳成功后,我們將會調用獲取所有文件數(shù)據的接口,并將獲取到的數(shù)據展示出來。
upload
函數(shù)代碼如下
const upload = (idx, file) => { let _progressInfos = [...progressInfosRef.current.val]; return UploadService.upload(file, (event) => { _progressInfos[idx].percentage = Math.round( (100 * event.loaded) / event.total ); setProgressInfos({ val: _progressInfos }); }) .then(() => { setMessage((prevMessage) => ([ ...prevMessage, "文件上傳成功: " + file.name, ])); }) .catch(() => { _progressInfos[idx].percentage = 0; setProgressInfos({ val: _progressInfos }); setMessage((prevMessage) => ([ ...prevMessage, "不能上傳文件: " + file.name, ])); }); };
每個文件的上傳進度信息根據 event.loaded
和 event.total
百分比值來計算,因為在調用 upload
函數(shù)的時候,已經將對應文件的索引傳遞進來了,所有我們根據對應的索引設置對應文件的上傳進度
除了這些工作,我們還需要在 Effect HookuseEffect()
做如下功能,這部分代碼的作用其實 componentDidMount
中起到的作用一致
const UploadFiles = () => { ... useEffect(() => { UploadService.getFiles().then((response) => { setFileInfos(response.data); }); }, []); ... }
到這里我們就需要將文件上傳的 UI 代碼添加上了,代碼如下
const UploadFiles = () => { ... return ( <div> {progressInfos && progressInfos.val.length > 0 && progressInfos.val.map((progressInfo, index) => ( <div className="mb-2" key={index}> <span>{progressInfo.fileName}</span> <div className="progress"> <div className="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 className="row my-3"> <div className="col-8"> <label className="btn btn-default p-0"> <input type="file" multiple onChange={selectFiles} /> </label> </div> <div className="col-4"> <button className="btn btn-success btn-sm" disabled={!selectedFiles} onClick={uploadFiles} > 上傳 </button> </div> </div> {message.length > 0 && ( <div className="alert alert-secondary" role="alert"> <ul> {message.map((item, i) => { return <li key={i}>{item}</li>; })} </ul> </div> )} <div className="card"> <div className="card-header">List of Files</div> <ul className="list-group list-group-flush"> {fileInfos && fileInfos.map((file, index) => ( <li className="list-group-item" key={index}> <a href={file.url}>{file.name}</a> </li> ))} </ul> </div> </div> ); };
在 UI 相關的代碼中, 我們使用了 Bootstrap 的進度條
使用 .progress
作為最外層包裝
內部使用 .progress-bar
顯示進度信息
.progress-bar
需要 style 按百分比設置進度信息
.progress-bar
進度條還可以設置 role
和 aria
屬性
文件列表信息的展示我們使用 map
遍歷 fileInfos
數(shù)組,并且將文件的 url
,name
信息展示出來
最后,我們將上傳文件組件導出
const UploadFiles = () => { ... } export default UploadFiles;
import React from "react"; import "./App.css"; import "bootstrap/dist/css/bootstrap.min.css"; import UploadFiles from "./components/UploadFiles.js" function App() { return ( <div className="container"> <h5> 使用 React 搭建文件上傳 Demo</h5> <div className="content"> <UploadFiles /> </div> </div> ); } export default App;
考慮到大多數(shù)的 HTTP Server 服務器使用 CORS 配置,我們這里在根目錄下新建一個 .env 的文件,添加如下內容
PORT=8081
到這里我們可以運行下前端項目了,使用命令 pnpm start
,瀏覽器地址欄輸入 http://localhost:8081/, ok 項目正常運行
文件選擇器、上傳按鈕、文件列表都已經可以顯示出來了,但還無法上傳。這是因為后端部分還沒有跑起來,接下來,我?guī)ьI大家手把手搭建上傳文件的后端部分。
后端部分我們使用 Nodejs + Express + Multer + Mongodb 來搭建文件上傳的項目,配合前端 Reactjs + Axios 來共同實現(xiàn)文件上傳功能。
后端項目我們提供以下幾個API
POST /upload
文件上傳接口
GET /files
文件列表獲取接口
GET /files/[filename]
下載指定文件
我們先使用命令 mkdir 創(chuàng)建一個空文件夾,然后 cd 到文件夾里面 這個文件夾就是我們的項目文件夾
mkdir nodejs-mongodb-upload-files cd nodejs-mongodb-upload-files
接著使用命令
npm init --yes
初始化項目,接著安裝項目需要的依賴包, 輸入如下命令
npm install express cors multer multer-gridfs-storage mongodb
package.js 文件
{ "name": "nodejs-mongodb-upload-files", "version": "1.0.0", "description": "Node.js upload multiple files to MongoDB", "main": "src/server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "node", "upload", "multiple", "files", "mongodb" ], "license": "ISC", "dependencies": { "cors": "^2.8.5", "express": "^4.17.1", "mongodb": "^4.1.3", "multer": "^1.4.3", "multer-gridfs-storage": "^5.0.2" } }
src/config/db.js
module.exports = { url: "mongodb://localhost:27017/", database: "files_db", filesBucket: "photos", };
src/middleware/upload.js
const util = require("util"); const multer = require("multer"); const { GridFsStorage } = require("multer-gridfs-storage"); const dbConfig = require("../config/db"); var storage = new GridFsStorage({ url: dbConfig.url + dbConfig.database, options: { useNewUrlParser: true, useUnifiedTopology: true }, file: (req, file) => { const match = ["image/png", "image/jpeg", "image/gif"]; if (match.indexOf(file.mimetype) === -1) { const filename = `${Date.now()}-zhijianqiu-${file.originalname}`; return filename; } return { bucketName: dbConfig.filesBucket, filename: `${Date.now()}-zhijianqiu-${file.originalname}` }; } }); const maxSize = 2 * 1024 * 1024; var uploadFiles = multer({ storage: storage, limits: { fileSize: maxSize } }).single("file"); var uploadFilesMiddleware = util.promisify(uploadFiles); module.exports = uploadFilesMiddleware;
這里我們定義一個 storage
的配置對象 GridFsStorage
url
: 必須是指向 MongoDB
數(shù)據庫的標準 MongoDB
連接字符串。multer-gridfs-storage
模塊將自動為您創(chuàng)建一個 mongodb
連接。
options
: 自定義如何建立連接
file
: 這是控制數(shù)據庫中文件存儲的功能。該函數(shù)的返回值是一個具有以下屬性的對象:filename, metadata, chunkSize, bucketName, contentType...
我們還檢查文件是否為圖像 file.mimetype
。bucketName
表示文件將存儲在 photos.chunks
和 photos.files
集合中。
接下來我們使用 multer
模塊來初始化中間件 util.promisify()
并使導出的中間件對象可以與 async-await
.
single()
帶參數(shù)的函數(shù)是 input 標簽的名稱
這里使用 Multer API
來限制上傳文件大小,添加 limits: { fileSize: maxSize }
以限制文件大小
controllers/flileUploadController.js
這個主要用于文件上傳,我們創(chuàng)建一個名 upload
函數(shù),并將這個函數(shù)導出去
我們使用 文件上傳中間件函數(shù)處理上傳的文件
使用 Multer 捕獲相關錯誤
返回響應
文件列表數(shù)據獲取和下載
getListFiles
: 函數(shù)主要是獲取 photos.files
,返回 url, name
download()
: 接收文件 name
作為輸入參數(shù),從 mongodb
內置打開下載流 GridFSBucket
,然后 response.write(chunk)
API 將文件傳輸?shù)娇蛻舳恕?/p>
const upload = require("../middleware/upload"); const dbConfig = require("../config/db"); const MongoClient = require("mongodb").MongoClient; const GridFSBucket = require("mongodb").GridFSBucket; const url = dbConfig.url; const baseUrl = "http://localhost:8080/files/"; const mongoClient = new MongoClient(url); const uploadFiles = async (req, res) => { try { await upload(req, res); if (req.file == undefined) { return res.status(400).send({ message: "請選擇要上傳的文件" }); } return res.status(200).send({ message: "文件上傳成功" + req.file.originalname, }); } catch (error) { console.log(error); if (error.code == "LIMIT_FILE_SIZE") { return res.status(500).send({ message: "文件大小不能超過 2MB", }); } return res.status(500).send({ message: `無法上傳文件:, ${error}` }); } }; const getListFiles = async (req, res) => { try { await mongoClient.connect(); const database = mongoClient.db(dbConfig.database); const files = database.collection(dbConfig.filesBucket + ".files"); let fileInfos = []; if ((await files.estimatedDocumentCount()) === 0) { fileInfos = [] } let cursor = files.find({}) await cursor.forEach((doc) => { fileInfos.push({ name: doc.filename, url: baseUrl + doc.filename, }); }); return res.status(200).send(fileInfos); } catch (error) { return res.status(500).send({ message: error.message, }); } }; const download = async (req, res) => { try { await mongoClient.connect(); const database = mongoClient.db(dbConfig.database); const bucket = new GridFSBucket(database, { bucketName: dbConfig.filesBucket, }); let downloadStream = bucket.openDownloadStreamByName(req.params.name); downloadStream.on("data", function (data) { return res.status(200).write(data); }); downloadStream.on("error", function (err) { return res.status(404).send({ message: "無法獲取文件" }); }); downloadStream.on("end", () => { return res.end(); }); } catch (error) { return res.status(500).send({ message: error.message, }); } }; module.exports = { uploadFiles, getListFiles, download, };
在 routes
文件夾中,使用 Express Router
在 index.js
中定義路由
const express = require("express"); const router = express.Router(); const uploadController = require("../controllers/flileUploadController"); let routes = app => { router.post("/upload", uploadController.uploadFiles); router.get("/files", uploadController.getListFiles); router.get("/files/:name", uploadController.download); return app.use("/", router); }; module.exports = routes;
POST /upload
: 調用 uploadFiles
控制器的功能。
GET /files
獲取/files圖像列表。
GET /files/:name
下載帶有文件名的圖像。
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("./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提供 Express 中間件以啟用具有各種選項的 CORS。
創(chuàng)建一個 Express 應用程序,然后使用方法添加cors中間件 在端口 8080 上偵聽傳入請求。
在項目根目錄下在終端中輸入命令 node src/server.js, 控制臺顯示
Running at localhost:8080
使用 postman 工具測試,ok 項目正常運行
文件上傳接口
文件列表接口
數(shù)據庫
在 nodejs-mongodb-upload-files 文件夾根目錄運行后端 Nodejs
node src/server.js
在 react-multiple-files-upload 文件夾根目錄運行前端 React
pnpm start
然后打開瀏覽器輸入前端訪問網址:
讀到這里,這篇“怎么使用Reactjs+Nodejs+Mongodb實現(xiàn)文件上傳功能”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。