持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
Reactjs + Nodejs + Express + Mongodb 搭建[图片上传/预览]前后端项目
概述
图片上传功能,在日常的项目开发中是很常见的功能,今天我们就来说说使用 Reactjs + Nodejs + Express + Mongodb 搭建前后端图片上传的例子
我们先看看完成后的效果
实现的功能:
- 图片上传功能
- 图片上传显示进度条功能
- 图片预览功能
- 图片列表功能
- 图片下载功能
前端使用的技术:
- Reactjs
- Bootstrap
- Axios
跟随本示例学习,你也可以搭建出来。
前端部分
前端项目结构
├── README.md
├── node_modules
├── package-lock.json
├── package.json
├── public
│ └── index.html
└── src
├── App.css
├── App.js
├── components
│ └── uploadImages.js
├── http-common.js
├── index.js
└── services
└── fileUpload.js
配置 React 环境
这里我们使用 npx 创建一个 React 项目
npx create-react-app react-upload-images
完成后,cd 进入项目根目录
安装 Axios
npm install axios
安装完成后,我们使用命令 npm start 启动项目,可以看到 项目已经正常启动了
这里我们使用 bootstrap 的样式,导入 Bootstrap 到项目中
打开文件 public/index.html ,将以下代码添加到 head 中
<link type="text/css" rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" />
配置 Axios
在 src 文件夹下新建文件 http-common.js, 并添加如下内容
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080",
headers: {
"Content-type": "application/json"
}
});
这里 baseURL 是你本地的地址
图片上传功能
前端项目的基本配置已经完成,接下来我们开始图片上传功能,在 src 文件下 创建如下文件
src/services/UploadFilesService.js,这个文件主要的作用就是和后端项目通讯,以进行文件的上传和文件列表数据的获取等
在文件中我们加入如下内容
import http from "../http-common";
class FileUploadService {
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 FileUploadService();
upload:用于将文件数据以post请求格式,FormData键值对的形式提交到后端,onUploadProgress则是用于获取进度条数据getFiles:用于获取服务器上的文件列表数据
创建图片上传组件
图片上传组件要满足功能有 图片上传,进度条,预览,提示信息等
首先建一个文件,并引入 import UploadService from "../services/fileUpload"
src/components/UploadFiles
代码如下:
import React, { Component } from "react";
import UploadService from "../services/UploadFilesService"
export default class UploadImages extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
</div>
);
}
}
接着我们在 constructor() 方法内部定义状态, 代码如下
export default class UploadImages extends Component {
constructor(props) {
super(props);
...
this.state = {
selectedFiles: undefined,
previewImages: [],
progressInfos: [],
message: [],
imageInfos: [],
};
}
}
状态定义好后,我们在添加一个选择图片的方法 selectFiles(),并添加 <input type="file" >,
export default class UploadImages extends Component {
...
selectFiles(event) {
let images = [];
for (let i = 0; i < event.target.files.length; i++) {
images.push(URL.createObjectURL(event.target.files[i]))
}
this.setState({
progressInfos: [],
message: [],
selectedFiles: event.target.files,
previewImages: images
});
}
}
我们使用 URL.createObjectURL() 用来获取图片预览 URL 并将其放入 previewImages 数组中, URL.createObjectURL()方法会创建一个 DOMString ,其中包含一个表示参数中提供的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定,
接着我们定义文件上传方法 upload
export default class UploadImages extends Component {
...
upload(idx, file) {
let _progressInfos = [...this.state.progressInfos];
UploadService.upload(file, (event) => {
_progressInfos[idx].percentage = Math.round((100 * event.loaded) / event.total);
this.setState({
progressInfos: _progressInfos,
});
})
.then(() => {
this.setState((prev) => {
let nextMessage = [...prev.message, `${file.name} 上传成功!`];
return {
message: nextMessage
};
});
return UploadService.getFiles();
})
.then((files) => {
this.setState({
imageInfos: files.data,
});
})
.catch(() => {
_progressInfos[idx].percentage = 0;
this.setState((prev) => {
let nextMessage = [...prev.message, "Could not upload the image: " + file.name];
return {
progressInfos: _progressInfos,
message: nextMessage
};
});
});
}
}
这里图片的上传进度根据 event.loaded 和计算 event.total,图片上传完毕后,我们调用 UploadService.getFiles(),来获取服务器上图片信息来展示,另外,我们要需要在 componentDidMount 钩子函数中调用,以便在初始状态时获取数据
export default class UploadImages extends Component {
...
componentDidMount() {
UploadService.getFiles().then((response) => {
this.setState({
imageInfos: response.data,
});
});
}
}
这里我们将界面的上传的 UI 渲染代码添加上
<div>
<div className="row">
<div className="col-8">
<label className="btn btn-default p-0">
<input type="file" accept="image/*" onChange={this.selectFiles} />
</label>
</div>
<div className="col-4">
<button
className="btn btn-success btn-sm"
disabled={!selectedFiles}
onClick={this.uploadImages}
>
上传
</button>
</div>
</div>
{progressInfos &&
progressInfos.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>
))}
{previewImages && (
<div>
{previewImages.map((img, i) => {
return <img className="preview" src={img} alt={"image-" + i} key={i}/>;
})}
</div>
)}
{message.length > 0 && (
<div className="alert alert-secondary mt-2" role="alert">
<ul>
{message.map((item, i) => {
return <li key={i}>{item}</li>;
})}
</ul>
</div>
)}
<div className="card mt-3">
<div className="card-header">图片列表</div>
<ul className="list-group list-group-flush">
{imageInfos &&
imageInfos.map((img, index) => (
<li className="list-group-item " key={index}>
<img src={img.url} alt={img.name} height="80px" />
<span className="ml-10"><a href={img.url} target="_blank">{img.name}</a></span>
</li>
))}
</ul>
</div>
</div>
在上面的代码中我们使用了 Boostrap 的进度条, 具体可查看 Bootstrap 文档。
将图片上传组件添加到App组件
打开 App.js 文件,将组件 UploadImages 引入并添加
import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import UploadImages from "./components/UploadFiles.js"
function App() {
return (
<div className="container">
<p>图片上传</p>
<div className="content">
<UploadImages />
</div>
</div>
);
}
export default App;
上传图片配置端口
考虑到大多数的 HTTP Server 服务器使用 CORS 配置,我们这里在根目录下新建一个 .env 的文件,添加如下内容
PORT=8081
项目运行
到这里我们可以运行下前端项目了,使用命令 npm start,浏览器地址栏输入 http://localhost:8081/, ok 项目正常运行
前端项目到这里,大部分已经完成了,接下来我们在下一篇文章中搭建后端部分,连通前后端。