当前位置:   article > 正文

从零实现一套低代码(保姆级教程)【后端服务】 --- 【19】实现Upload相关接口和前端页面_upload接口

upload接口

摘要

在之前的文章里,在实现组件的时候,有提到Upload组件,当时并没有对这个组件进行实现。
那我们现在思考一下,如果我们想实现Upload组件和Image组件,我们应该怎么去做。

Upload组件有一个很重要的属性是action,也就是上传的服务器位置。所以我们实现Upload组件之前,应该有一个服务器用来保存上传的图片。之后在Image组件里进行展示。

那这整套流程在我们的三个项目里是怎么样的呢?

XinBuilderServer:实现图片上传的功能,用来保存上传的图片。
AppBuilder: 实现图片上传的前端UI,支持查看上传的图片。
XinBuilder:在设计器里,支持Upload组件和Image组件。

在这里插入图片描述

OK,大体流程我们有了,现在我们开始进行实现。

1.实现Upload相关的接口 – XinBuilderServer

现在我们来说一下,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
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

定义好接口之后我们来到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 { }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

这段代码的意思就是,我们存放在了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
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

只需要获取到public下的文件目录即可。

然后回到controller中,我们补充一下接口:

  @Post('findAllImage')
  @ApiOperation({summary: '获取图片列表'})
  findAllImage () {
    return this.uploadService.findAllImage()
  }
  • 1
  • 2
  • 3
  • 4
  • 5

OK,现在我们可以将图片上传到服务器上了,但是我们怎么访问呢,比如我知道了文件名称,我们怎么访问这个文件呢?

我们要修改一下访问服务器的静态路径,来到main.ts中:

我们给app添加一下静态路径,也就是当你访问localhost:4000的时候,访问的就是public目录
  app.useStaticAssets('public');
  • 1

到这里,我们上传图片和查看图片列表的接口,就完成了。

在这里插入图片描述

相关的代码提交在github上:
https://github.com/TeacherXin/XinBuilderServer2
commit: fix: 第三节,实现图片上传和查看图片列表的接口

2.实现图片管理的功能 – AppBuilder

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在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>
  )
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125

相关的代码提交在github上:
https://github.com/TeacherXin/AppBuilder
commit: fix: 第二节:实现上传图片以及查看图片列表详情的路由页面

博主补充

有了上面的基础,我们就可以在下一篇中实现Upload组件和Image组件了。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号