赞
踩
在之前的文章里,在实现组件的时候,有提到Upload组件,当时并没有对这个组件进行实现。
那我们现在思考一下,如果我们想实现Upload组件和Image组件,我们应该怎么去做。
Upload组件有一个很重要的属性是action,也就是上传的服务器位置。所以我们实现Upload组件之前,应该有一个服务器用来保存上传的图片。之后在Image组件里进行展示。
那这整套流程在我们的三个项目里是怎么样的呢?
XinBuilderServer:实现图片上传的功能,用来保存上传的图片。
AppBuilder: 实现图片上传的前端UI,支持查看上传的图片。
XinBuilder:在设计器里,支持Upload组件和Image组件。
OK,大体流程我们有了,现在我们开始进行实现。
现在我们来说一下,Upload上传的接口和之前我们实现的接口有什么区别。对于之前我们添加页面等接口,都是将数据存在数据库里面。
但是对于图片,我们不应该存在数据库里,因为他不是一个字符串或者JSON。所以我们采用服务端的存储,就是在服务端创建一个文件夹用来保存上传的图片。我们来到XinBuilderServer项目中,创建uploadImage模块:
先来到controller中,我们实现一下上传图片的接口:
import { Controller, Post,UseInterceptors,UploadedFile } from '@nestjs/common'; import { UploadService } from './upload-image.service'; // FileInterceptor用于单文件上传,FilesInterceptor用于多文件上传 import {FileInterceptor} from '@nestjs/platform-express' import { ApiTags, ApiOperation } from '@nestjs/swagger' @Controller('upload') @ApiTags('图片管理') export class UploadController { constructor(private readonly uploadService: UploadService) {} @Post('album') @ApiOperation({summary: '添加图片'}) // UseInterceptors 处理文件的中间件,file是一个标识名 @UseInterceptors(FileInterceptor('file')) // UploadedFile装饰器是用于读取文件的 upload (@UploadedFile() file) { console.log("file:",file) return file } }
定义好接口之后我们来到module中,对上传的图片进行处理,当你拿到图片之后,我们应该存放在定义好的目录里面:
import { Module } from '@nestjs/common'; import { UploadService } from './upload-image.service'; import { UploadController } from './upload-image.controller'; //文件上传需要的包 import { MulterModule } from '@nestjs/platform-express'; import { diskStorage } from 'multer'; import { join } from 'path'; const path = require('path') const iconv = require('iconv-lite'); @Module({ //里面有register 和 registerAsync 两个方法,前者是同步的,后者是异步的 imports: [MulterModule.register({ //图片上传完要存放的位置 storage: diskStorage({ destination: join(path.resolve(__dirname, '../../') + '/public', 'images'),//存放的文件路径 filename: (req, file, callback) => { //重新定义文件名,file.originalname 文件的原始名称 // extname 获取文件后缀 let fileName = iconv.decode(file.originalname, 'utf-8'); //返回新的名称,参数1是错误,这里用null就好 return callback(null, fileName) } }), } )], controllers: [UploadController], providers: [UploadService] }) export class UploadModule { }
这段代码的意思就是,我们存放在了src下的public下的images中:
OK,这样上传图片的接口,我们就写好了(不要问为什么这么写,博主也不太懂,单纯的在网上找的。。。o.O)
有了上传图片的接口之后,我们还要写一个获取图片列表的接口,我们来到service中:
import { Injectable } from '@nestjs/common';
const fs = require('fs')
import { join, extname } from 'path';
const path = require('path')
@Injectable()
export class UploadService {
findAllImage() {
const imagePath = join(path.resolve(__dirname, '../..') + '/public', 'images')
const list = fs.readdirSync(imagePath);
return list
}
}
只需要获取到public下的文件目录即可。
然后回到controller中,我们补充一下接口:
@Post('findAllImage')
@ApiOperation({summary: '获取图片列表'})
findAllImage () {
return this.uploadService.findAllImage()
}
OK,现在我们可以将图片上传到服务器上了,但是我们怎么访问呢,比如我知道了文件名称,我们怎么访问这个文件呢?
我们要修改一下访问服务器的静态路径,来到main.ts中:
我们给app添加一下静态路径,也就是当你访问localhost:4000的时候,访问的就是public目录 app.useStaticAssets('public');
到这里,我们上传图片和查看图片列表的接口,就完成了。
相关的代码提交在github上:
https://github.com/TeacherXin/XinBuilderServer2
commit: fix: 第三节,实现图片上传和查看图片列表的接口
OK,现在我们有了上传图片和查看图片列表的接口,我们来到AppBuilder中,把前端的UI界面进行实现。
我们在主页面中,添加一个图片管理,作为入口。
增加一个路由用来写图片相关的页面:
app-builder\src\index.tsx
import React, { Suspense } from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import Page from './routes/page'; import UploadImage from './routes/uploadImage'; import { HashRouter as Router, Routes , Route} from "react-router-dom"; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <Router> <Suspense> <Routes> <Route path={'/'} element={<Page />}></Route> <Route path={'/uploadImage'} element={<UploadImage />}></Route> </Routes> </Suspense> </Router> );
在UploadImage组件中,我们要做到的效果是:
可以上传查看图片,由于组件的实现不是很难,所以这里我把源码贴出来,附加注释。
import { useState, useEffect } from 'react' import { Divider, Image, message, Upload } from 'antd' import { FileImageOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons' import axios from 'axios' import './index.css' const getBase64 = (img: any,callback: Function) => { const reader = new FileReader(); reader.addEventListener("load", () => callback(reader.result)); reader.readAsDataURL(img); } const beforeUpload =(file: any) => { const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png"; if (!isJpgOrPng) { message.error("You can only upload JPG/PNG file!"); } const isLt2M = file.size / 1024 / 1024 < 2; if (!isLt2M) { message.error("Image must smaller than 2MB!"); } return isJpgOrPng && isLt2M; } export default function UploadImage() { const [imageList, setImageList] = useState([]) const [img, setImg] = useState(""); const [loading, setLoading] = useState(false); useEffect(() => { getImageList() }, []) /** * 获取图片列表 */ const getImageList = () => { axios.post('http://localhost:4000/upload/findAllImage') .then(res => { if(res.data) { setImageList(res.data) } }) } /** * 上传图片的按钮 */ const uploadButton = ( <div> {loading ? <LoadingOutlined /> : <PlusOutlined />} <div style={{ marginTop: 8 }}>Upload</div> </div> ); /** * 上传图片后,更新展示图片 * @param info 上传图片的信息 * @returns */ const handleChange = (info: any) => { if (info.file.status === "uploading") { setLoading(true); return; } if (info.file.status === "done") { getBase64(info.file.originFileObj, () => { setLoading(false); setImg(`http://localhost:4000/images/` + info.file.response.filename); getImageList() }); } }; /** * 点击图片列表中的某一项时,更新Image组件 * @param imageName 图片名称 * @returns */ const getImage = (imageName: string) => { return () => { setImg(`http://localhost:4000/images/` + imageName); } } return ( <div className='PageList'> <div className='pageLeft'> <div className='leftHeader'>XinBuilder</div> <div className='leftDiscribe'>图片管理平台</div> <Divider /> <div> { imageList.map((item, index) => { return <div style={img.includes(item) ? {backgroundColor:'#edeaeb'} : {}} onClick={getImage(item)} key={index} className='imageItem'> <FileImageOutlined style={{marginRight:'10px'}}/> {item} </div> }) } </div> </div> <div className='imageRight'> <Upload name="file" listType="picture-card" showUploadList={false} action={`http://localhost:4000/upload/album`} beforeUpload={beforeUpload} onChange={handleChange} > { uploadButton } </Upload> <Image width={500} src={img} /> </div> </div> ) }
相关的代码提交在github上:
https://github.com/TeacherXin/AppBuilder
commit: fix: 第二节:实现上传图片以及查看图片列表详情的路由页面
有了上面的基础,我们就可以在下一篇中实现Upload组件和Image组件了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。