乐趣区

关于node.js:React-Nodejs-全栈实战教程-手把手教你搭建文件上传管理后台

本教程手把手率领大家搭建一套通过 React + Node.js + Mongodb 上传文件的后盾零碎,只有你追随本教程一步步走,肯定能很好的了解整个前后端上传文件的代码逻辑。前端咱们应用 Reactjs + Axios 来搭建前端上传文件利用,后端咱们应用 Node.js + Express + Multer + Mongodb 来搭建后端上传文件解决利用。

当然,本教程还会教给大家如何写一个能够限度上传文件大小、有百分比进度条、可报错、可显示服务器上文件列表、可点击下载文件的前端操作界面。

最初实现的上传文件工具后盾如下图,追随本教学习,你也能够搭建进去。

全栈实战教程:

  • Vue + Node.js+Expres+MySQL 开发「待办清单」APP
  • Vue + Axios + Node.js + Express 搭建带预览的「上传图片」治理后盾
  • Vue + Axios + Node.js + Express 搭建「文件上传」治理后盾
  • React + Nodejs 搭建带预览的「上传图片 / 预览」治理后盾
  • React + Axios + Node.js + Express 搭建「文件上传」治理后盾

后端实战教程:

  • 应用 Node.js + MySQL 开发 RESTful API 接口(Node.js + Express + Sequelize + MySQL)
  • 应用 Node.js + MongoDB 开发 RESTful API 接口(Node.js + Express + MongoDB)

如果你正在搭建后盾管理工具,又不想解决前端问题,举荐应用卡拉云,卡拉云是新一代低代码开发工具,可一键接入常见数据库及 API,无需懂前端,仅需拖拽即可疾速搭建属于你本人的后盾管理工具,一周工作量缩减至一天,详见本文文末。

React + Node.js + Mongodb「上传文件」前后端我的项目构造

前端我的项目构造

├── 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

Reactjs 前端局部

  • App.js: 把咱们的组件导入到 React 的起始页
  • components/UploadFiles.js: 文件上传组件
  • http-common.js: 应用 HTTP 根底 Url 和标头初始化 Axios。
  • 咱们在.env 中为咱们的应用程序配置端口
  • services/UploadFilesService.js: 这个文件中的函数用于文件上传和获取数据库中文件数据

后端我的项目构造

├── 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、数据库、文件存储桶)。
  • middleware/upload.js:初始化 Multer GridFs 存储引擎(包含 MongoDB)并定义中间件函数。
  • controllers/flileUploadController.js:配置 Rest API
  • routes/index.js:路由,定义前端申请后端如何执行
  • server.js:Node.js 入口文件

扩大浏览:《React Echarts 应用教程 – 如何在 React 中退出图表》

✦ 前端局部 - 上传文件 React + Axios

配置 React 环境

这里咱们应用 pnpm vite 创立一个 React 我的项目

npx create-react-app kalacloud-react-multiple-files-upload

我的项目创立实现后,cd 进入我的项目,装置我的项目运行须要的依赖包和 Axios 终端别离顺次如下命令

pnpm install

pnpm install axios

执行实现咱们启动我的项目 pnpm start

能够看到控制台中曾经输入了信息,在浏览器地址栏中输出控制台输入的地址,我的项目曾经跑起来了

导入 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;

扩大浏览:《7 款最棒的开源 React 挪动端 UI 组件库和模版框架》

初始化 Axios HTTP 客户端

src 目录下 咱们新建 http-common.js 文件,并增加如下代码

import axios from "axios";

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

这里 baseURL 的地址是咱们后端服务器的 REST API 地址,要依据集体理论状况进行批改。本教程后文,教你搭建上传文件的后端局部,请持续浏览。

创立「上传文件」性能

src/services/UploadFilesService.js, 这个文件次要的作用就是和后端我的项目通信,以进行文件的上传和文件列表数据的获取等。

在文件中咱们退出如下内容

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,并定义一个对象,在对象中增加两个属性函数,作用如下

  • upload:函数以 POST 的形式将数据提交到后端,接管两个参数 fileonUploadProgress

    • file 上传的文件,以 FormData 的模式上传
    • onUploadProgress 文件上传进度条事件,监测进度条信息
  • getFiles:函数用于获取存储在 Mongodb 数据库中的数据

最初将这个对象导出去。

扩大浏览:《7 款最棒的开源 React UI 组件库和模版框架测评》

创立 React 多文件上传组件

接下来咱们来创立文件上传组件,首先组件要满足性能有文件上传,上传进度条信息展现,文件预览,提示信息,文件下载等性能

这里咱们应用 React Hooks 和 useState 来创立文件上传组件,创立文件 src/components/UploadFiles,增加如下代码

import React, {useState, useEffect, useRef} from "react";
import UploadService from "../services/UploadFilesService";
const UploadFiles = () => {return ();
};
export default UploadFiles;

而后咱们应用 React Hooks 定义状态

const UploadFiles = () => {const [selectedFiles, setSelectedFiles] = useState(undefined);
    const [progressInfos, setProgressInfos] = useState({val: [] });
    const [message, setMessage] = useState([]);
    const [fileInfos, setFileInfos] = useState([]);
    const progressInfosRef = useRef(null)
}

状态定义好后,咱们在增加一个获取文件的办法 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 办法将可迭代数据转换数组模式的数据,接着应用 map 办法将文件的进度信息,名称信息存储到 _progressInfos

接着咱们应用 map 办法调用 files 数组中的每一项,使 files 中的每一项都通过 upload 函数的解决,在 upload 函数中咱们会返回上传文件申请函数 UploadService.uploadPromise 状态

所以 uploadPromises 中存储的就是处于 Promise 状态的上传文件函数,接着咱们应用 Promise.all 同时发送多个文件上传申请,在所有文件都上传胜利后,咱们将会调用获取所有文件数据的接口,并将获取到的数据展现进去。

upload 函数代码如下

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.loadedevent.total 百分比值来计算,因为在调用 upload 函数的时候,曾经将对应文件的索引传递进来了,所有咱们依据对应的索引设置对应文件的上传进度

除了这些工作,咱们还须要在 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 进度条还能够设置 rolearia 属性

文件列表信息的展现咱们应用 map 遍历 fileInfos 数组,并且将文件的 url,name 信息展现进去

最初,咱们将上传文件组件导出

const UploadFiles = () => {...}
export default UploadFiles;

扩大浏览:《最好用的 8 款 React Datepicker 工夫日期选择器测评举荐》

将文件上传组件增加到 App 组件

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">
      <h4> 应用 React 搭建文件上传 Demo</h4>
      <p> <a href="https://kalacloud.com/"> 卡拉云 </a>- 低代码开发工具 1 秒搭建 </p>
      <p> 应用卡拉云无需懂任何前端技术,仅需拖拽即可搭建属于你的后盾管理系统 </p>
      <div className="content">
        <UploadFiles />
      </div>
    </div>
  );
}

export default App;

上传文件配置端口

思考到大多数的 HTTP Server 服务器应用 CORS 配置,咱们这里在根目录下新建一个 .env 的文件,增加如下内容

运行 React 我的项目

到这里咱们能够运行下前端我的项目了,应用命令 pnpm start,浏览器地址栏输出 http://localhost:8081/, ok 我的项目失常运行

文件选择器、上传按钮、文件列表都曾经能够显示进去了,但还无奈上传。这是因为后端局部还没有跑起来,接下来,我率领大家手把手搭建上传文件的后端局部。

React 前端「文件上传」源码

你能够在咱们的 github 上下载到残缺的 React 图片上传 Demo。

当然你也能够不必这么吃力搭建前端做图片上传性能,间接应用卡拉云,无需懂前后端,简略拖拽即可生成一套属于你本人的后盾管理工具。

扩大浏览:《React form 表单验证终极教程》

✦ 后端局部 – 文件上传 Node.js + Express + Multer + MongoDB

后端局部咱们应用 Nodejs + Express + Multer + Mongodb 来搭建文件上传的我的项目,配合前端 Reactjs + Axios 来独特实现文件上传性能。

后端我的项目咱们提供以下几个 API

  • POST /upload 文件上传接口
  • GET /files 文件列表获取接口
  • GET /files/[filename] 下载指定文件

配置 Node.js 开发环境

咱们先应用命令 mkdir 创立一个空文件夹,而后 cd 到文件夹外面 这个文件夹就是咱们的我的项目文件夹

mkdir kalacloud-nodejs-mongodb-upload-files
cd kalacloud-nodejs-mongodb-upload-files

接着应用命令

初始化我的项目,接着装置我的项目须要的依赖包, 输出如下命令

npm install express cors multer multer-gridfs-storage mongodb

package.js 文件

{
  "name": "kalacloud-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"
  }
}

扩大浏览:《React Router 6 (React 路由) 最具体教程》

配置 MongoDB 数据库

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()}-kalacloud-${file.originalname}`;
      return filename;
    }
    return {
      bucketName: dbConfig.filesBucket,
      filename: `${Date.now()}-kalacloud-${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 数据库的规范 MongoDB 连贯字符串。multer-gridfs-storage 模块将主动为您创立一个 mongodb 连贯。
  • options: 自定义如何建设连贯
  • file: 这是管制数据库中文件存储的性能。该函数的返回值是一个具备以下属性的对象:filename, metadata, chunkSize, bucketName, contentType... 咱们还查看文件是否为图像 file.mimetypebucketName 示意文件将存储在 photos.chunksphotos.files 汇合中。
  • 接下来咱们应用 multer 模块来初始化中间件 util.promisify() 并使导出的中间件对象能够与 async-await.
  • single() 带参数的函数是 input 标签的名称
  • 这里应用 Multer API 来限度上传文件大小,增加 limits: {fileSize: maxSize} 以限度文件大小

扩大浏览:《最好的 6 个 React Table 组件具体亲测举荐》

创立文件上传的控制器

controllers/flileUploadController.js

这个文件次要用于文件上传,咱们创立一个名 upload 函数,并将这个函数导出去

  • 咱们应用 文件上传中间件函数解决上传的文件
  • 应用 Multer 捕捉相干谬误
  • 返回响应

文件列表数据获取和下载

  • getListFiles: 函数次要是获取 photos.files, 返回 url,name
  • download(): 接管文件 name 作为输出参数,从 mongodb 内置关上下载流 GridFSBucket,而后 response.write(chunk) API 将文件传输到客户端。
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,
};

扩大浏览:《React Draggable 实现拖拽 – 最具体中文教程》

定义 routes 路由

routes 文件夹中,应用 Express Routerindex.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 下载带有文件名的图像。

创立 Express 服务器

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}`);
});

这里咱们导入了 ExpressCors,

  • Express 用于构建 Rest api
  • Cors 提供 Express 中间件以启用具备各种选项的 CORS。创立一个 Express 应用程序,而后应用办法增加 cors 中间件 在端口 8080 上侦听传入申请。

运行我的项目并测试

在我的项目根目录下在终端中输出命令 node src/server.js, 控制台显示

Running at localhost:8080

应用 postman 工具测试,ok 我的项目失常运行

文件上传接口

文件列表接口

MongoDB 数据库

React + Node.js 上传文件前后端一起运行

在 kalacloud-nodejs-mongodb-upload-files 文件夹根目录运行后端 Nodejs

在 kalacloud-react-multiple-files-upload 文件夹根目录运行前端 React

而后关上浏览器输出前端拜访网址:

到这里整个前后端「上传文件」管理工具就搭建实现了。

Node.js 后端「文件上传」源码 你能够在咱们的 github 上下载到残缺的 Node.js 后端「文件上传」源码。

如果你还没搞懂,也不必焦急,间接应用卡拉云,无需懂任何前后端技术,仅需简略的鼠标拖拽即可疾速生成包含「文件上传」治理在内的任何后盾管理工具。

「文件上传」前后端搭建总结及卡拉云

本教程手把手教大家搭建 React 前端 + Node.js 后端 的「文件上传」管理工具,如果你一步步跟着走,肯定曾经把 Demo 跑起来了。但如果你会应用最新的低代码开发工具「卡拉云」,齐全不须要这么繁琐,仅需 1 分钟,就能搭建起属于本人的「文件上传」管理工具。

立刻开明卡拉云,从侧边工具栏间接拖拽组件到页面,生成上传组件和文件管理工具。1 分钟搞定「上传文件」管理工具。

再看个卡拉云的 Demo 案例,上面是用卡拉云搭建的数据库 CURD 后盾管理系统,只需拖拽组件,即可在 10 分钟内实现搭建。

可间接分享给共事一起应用:https://my.kalacloud.com/apps/8z9z3yf9fy/published

卡拉云可帮你疾速搭建企业外部工具,下图为应用卡拉云搭建的外部广告投放监测零碎,无需懂前端,仅需拖拽组件,10 分钟搞定。你也能够疾速搭建一套属于你的后盾管理工具。

卡拉云是新一代低代码开发平台,与前端框架 Vue、React 等相比,卡拉云的劣势在于不必首先搭建开发环境,间接注册即可开始应用。开发者齐全不必解决任何前端问题,只需简略拖拽,即可疾速生成所需组件,可一键接入常见数据库及 API,依据疏导简略几步买通前后端,数周的开发工夫,缩短至 1 小时。立刻收费试用卡拉云。

全栈实战教程:

  • Vue + Node.js+Expres+MySQL 开发「待办清单」APP
  • Vue + Axios + Node.js + Express 搭建带预览的「上传图片」治理后盾
  • Vue + Axios + Node.js + Express 搭建「文件上传」治理后盾
  • React + Nodejs 搭建带预览的「上传图片 / 预览」治理后盾
  • React + Axios + Node.js + Express 搭建「文件上传」治理后盾

后端实战教程:

  • 应用 Node.js 开发 RESTful API 接口(Node.js + Express + Sequelize + MySQL)
退出移动版