手把手教你做测开:开发Web平台之图书新增

2514 篇文章 34 订阅
2030 篇文章 14 订阅

在前几篇文章中,我们主要讲了开发Web平台之环境准备、登录认证、用户信息管理、接口文档、图书信息、图书下架,图书修改。

至此我们图书系统的CURD中的URD功能已初步实现,只剩下C也就是新增功能。因此,本篇文章将完成我们这个系列的最后一个功能,图书新增功能的实现。

如上文所说,我们这个系列的教程不会教你搭建一个完完整整的系统,但会将最基本的方法教给你,掌握了这些方法,你可以在此基础上扩展更多的功能。我们先来看一下这篇文章要实现的内容:

1.图片存储至阿里云OSS

2.豆瓣图书新增功能的后端实现

3.豆瓣图书新增功能的前端实现

4.打包

图片存储至阿里云OSS

新增功能需要的填写字段

如果我们要完成豆瓣图书的新增功能,那么我们要在新增页面填写哪些内容呢?至少前端页面上的这些信息是要具备的:

  1. title 书名

  2. score 评分

  3. img_src 图片链接

  4. publish_detail 出版信息

  5. slogan 简介

图片

图片信息存储位置

其他的信息可以在前端通过输入框输入,图片信息是一个链接,当然你也可以输入一个网上现成的图片链接,但是还是做成上传图片的形式比较好,因为这样相对比较方便,直接上传图片就行,不用费力去网上找链接。

那问题来了?上传的图片要存到哪里?其实如果只是你自己访问,那么这个网站的任何静态资源都可以存储在本地,如果要开放给其他人访问,那么最好这些图片信息能放在一个大家都能访问到的地方,阿里云存储对象OSS服务给我们提供了这样一个API服务,你可以上传图片到阿里云,然后它会给你返回图片的链接。

开通阿里云OSS服务

首先你要申请一个阿里云账号,然后实名认证后,开通阿里云OSS服务。这个服务一年费用相对比较便宜,网上也有各种操作教程,因此这里暂时不做操作演示。

创建Bucket

1.进入工作台,创建Bucket,Bucket名称可以随便取,只要不重复就像,读取权限使用公开读,其他采用默认就行。

图片

2.创建成功之后,你会看到阿里云给我们提供了一个外网访问的地域节点Endpoint和Bucket名字,一会我们会用到。

图片

3.点击左侧的文件管理,会发现文件管理里是空的,什么都没有,因为还没有存放图片。

图片

查看API

通过网址:https://help.aliyun.com/document_detail/88426.html,打开对象存储OSS的API,它给我们提供的上传本地文件的方法,写得非常详细。

图片

查看AccessKey

在工作台上方点头像,进入AccessKey管理。如果你已经创建过AccessKey,直接点击查看Secret就可以,如果没有创建,先创建一个AccessKey,创建好之后就可以查看AccessKey ID和AccessKey Secret。

图片

图片

豆瓣图书新增功能的后端实现

编写脚本进行上传

安装oss2:

pip install oss2

在backend目录下的utils目录下,创建一个aliyun_oss的py文件,写入以下内容:

 import oss2
import os

class AliyunOss(object):

    def __init__(self):
        #access_key
        self.access_key_id = "xxxxxx"
        self.access_key_secret = "xxxxxx"
        self.auth = oss2.Auth(self.access_key_id, self.access_key_secret)
        #bucket name
        self.bucket_name = "book-image-store"
        #endpoint
        self.endpoint = "oss-cn-hangzhou.aliyuncs.com"
        self.bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)

    #定义本地上传图片的方法:name表示上传到阿里云OSS后显示的图片名字,file表示图片的路径
    def put_object_from_file(self, name, file):
        self.bucket.put_object_from_file(name, file)
        img_src =  "https://{}.{}/{}".format(self.bucket_name, self.endpoint, name)
        return img_src


if __name__ == '__main__':
    aliyunoss = AliyunOss()
    img_src = aliyunoss.put_object_from_file("katong.jpeg", r"C:\Users\beck\Desktop\a51034ceb9be6492bf75fbb18bdfa5a8.jpeg")
    print(img_src)

运行之后,发现阿里云有了图片,并且返回了一个图片链接https://book-image-store.oss-cn-hangzhou.aliyuncs.com/katong.jpeg。

图片

新的问题:如果每次在脚本里把图片名固定为katong.jpeg,那么传多张相同图片的时候会无法区分,因此,我们需要将name设置为可变的,即使是同一张图片传多次,也能保证图片名不会重复。

这里我们可以使用os.path.basename方法拿到图片名,然后给它拼接一个uuid,可以思考一下,这里为什么要用方法self.bucket.put_object,而不是self.bucket.put_object_from_file?

def put_object_from_file(self, file):
    name = str(uuid.uuid1()) + os.path.basename(str(file))
    self.bucket.put_object(name, file)
    img_src =  "https://{}.{}/{}".format(self.bucket_name, self.endpoint, name)
    return img_src

可以看到后面的图片名是一样的,只是前面的uuid不同。

图片

修改book视图

在book/views.py中单独定义一个上传方法,这个方法是post请求,它的内部直接调用utils/aliyun_oss中的AliyunOss类,将图片上传到阿里云后,将图片地址返回。

由于上传图片的方法,ModelViewSet中没有提供,因此需要自定义action,相当于除了ModelViewSet中提供的最基本的CURD接口外,我们可以自定义需要的接口。

定义完成之后,可以在swagger中查看一下。一般来说,action定义的接口,如果没有指定url,那么它的方法名会作为url的一部分。

图片

豆瓣图书新增功能的前端实现

前端思路分析

在前端开始之前,我们先了解一下我们需要的样式。

如果要新增一个功能,我们前端肯定要写成表单的形式,表单内可以输入内容,表单下方有提交功能,同时表格内还要嵌入上传图片的组件,当图片加载后,会通过调用后端上传图片的接口将返回的图片链接传给前端,当所有内容输入完成后,会通过提交按钮,将所有填写的内容传给封装好的新增方法,新增方法调用后端新增接口,完成图片的新增,最后,这个页面会跳转到图书展示页面。

因此,前端需要完成以下的任务:

  1. 前端页面的创建

  2. 前端上传图片公共组件的创建

  3. 前端封装公共方法

  4. 修改路由

新增views/book/create.vue

先来看前端页面的创建,我们先看一个elemementui提供的form表单组件,地址:https://element.eleme.cn/#/zh-CN/component/form。这个是比较符合我们的情况。

图片

再来看一个vue-element-admin提供的组件,这个也是比较符合我们对上传组件的要求。

图片

接下来,新增views/book/create.vue,然后写入内容,需要说明一下的是:

1.SingleImage3 是上传图片的公共组件,你可以从vue-element-admin的src/components下将整个Upload目录全部复制到自己项目对应的目录下。

2.createDouBanBookAPI是我们预定义的封装好的新增方法,这个方法里面的this.$notify表示提交成功后出现一个成功的提示。

3.this.$router.push(‘/douban/book/’),表示提交成功后跳转到图书展示页面。

4.这一句中使用了@get_tempUrl来触发get_imgSrc来获得子组件SingleImage3 传给父组件的值,将该值最终传递给bookForm.img_src。

<template>
  <div>
    <el-form ref="form" :model="bookForm" label-width="80px" size="large" style="margin-top: 50px">
      <el-form-item label="书  名">
        <el-input v-model="bookForm.title" style="width: 300px"></el-input>
      </el-form-item>
      <el-form-item label="评  分">
        <el-input v-model="bookForm.score" style="width: 300px"></el-input>
      </el-form-item>
      <el-form-item label="简  介">
        <el-input v-model="bookForm.slogan" style="width: 300px"></el-input>
      </el-form-item>
      <el-form-item label="出版信息">
        <el-input v-model="bookForm.publish_detail" style="width: 300px"></el-input>
      </el-form-item>
      <el-form-item label="上传封面" prop="img_src" style="margin-bottom: 30px;">
          <SingleImage3 v-model="bookForm.img_src" @get_tempUrl="get_imgSrc" />
      </el-form-item>
      <el-form-item size="large">
        <el-button type="primary" @click="onSubmit">立即创建</el-button>
        <el-button>取消</el-button>
      </el-form-item>
    </el-form>

  </div>
</template>


<script>

  import SingleImage3 from '@/components/Upload/SingleImage3'
  import * as DouBanBook from '@/api/douban-book'

  export default {
    name: 'create',
    components: { SingleImage3 },
    data() {
      return {
        bookForm: {
          title: '',
          score: '',
          slogan: '',
          publish_detail: '',
          img_src: '',
        }
      };
    },
    methods: {
      onSubmit() {
        DouBanBook.createDouBanBookAPI(this.bookForm)
          .then(res => {
              this.$notify({
              title: 'Success',
              message: '提交成功!',
              type: 'success',
              duration: 2000
            })
            this.$router.push('/douban/book/')

          })
      },

      get_imgSrc(data) {
        this.bookForm.img_src = data
      }
    }
  };
</script>

修改components/Upload/SingleImage3.vue

components下的Upload目录是从中复制下来的,但需要做一些修改:

修改点-1:将beforeUpload中原来的获得key, token等方法去掉,换成限制图片大大小、类型。

修改点-2:通过action直接跟着后端的接口地址,容易引起跨域的问题,因此需要通过http-request来请求后端上传图片的接口。

修改点-3:封装上传图片的方法,方法中通过调用后端接口,来实现图片的上传,将后端接口返回的图片链接通过this.$emit传给父组件。

<template>
  <div class="upload-container">
    <el-upload
      :multiple="false"
      :show-file-list="false"
      :on-success="handleImageSuccess"
      class="image-uploader"
      drag
      action= ''
    //修改点-2
      :http-request="(param) => uploadDouBanBookImage(param)"
    >
      <i class="el-icon-upload" />
      <div class="el-upload__text">
        将文件拖到此处,或<em>点击上传</em>
      </div>
    </el-upload>
    <div class="image-preview image-app-preview">
      <div v-show="imageUrl.length>1" class="image-preview-wrapper">
        <img :src="imageUrl">
        <div class="image-preview-action">
          <i class="el-icon-delete" @click="rmImage" />
        </div>
      </div>
    </div>
    <div class="image-preview">
      <div v-show="imageUrl.length>1" class="image-preview-wrapper">
        <img :src="imageUrl">
        <div class="image-preview-action">
          <i class="el-icon-delete" @click="rmImage" />
        </div>
      </div>
    </div>
  </div>
</template>

<script>

import * as DouBanBook from '@/api/douban-book'

export default {
  name: 'SingleImageUpload3',
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      tempUrl: '',
    }
  },
  computed: {
    imageUrl() {
      return this.value
    }
  },
  methods: {
    rmImage() {
      this.emitInput('')
    },
    emitInput(val) {
      this.$emit('input', val)
    },
    handleImageSuccess(file) {
      this.emitInput(file.files.file)
    },
    //修改点-1
    beforeUpload(file) {
      const isPic = file.type.indexOf('image') >= 0;
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isPic) {
        this.$message.error('上传的文件只能为图片格式!')
      }
      if (!isLt2M) {
        this.$message.error('上传图片大小不能超过 2MB!')
      }

    },
    //修改点-3
    uploadDouBanBookImage(param) {
      const formData = new FormData()
      formData.append('file', param.file)
      console.log(param.file)
      DouBanBook.uploadDouBanBookImageAPI(formData)
        .then(response => {
          console.log('上传图片成功')
          this.value = response.data.img_src
          this.tempUrl = this.value
          this.$emit('get_tempUrl', this.tempUrl)
        })

    }
  }
}
</script>

<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.upload-container {
  width: 100%;
  position: relative;
  @include clearfix;
  .image-uploader {
    width: 35%;
    float: left;
  }
  .image-preview {
    width: 200px;
    height: 200px;
    position: relative;
    border: 1px dashed #d9d9d9;
    float: left;
    margin-left: 50px;
    .image-preview-wrapper {
      position: relative;
      width: 100%;
      height: 100%;
      img {
        width: 100%;
        height: 100%;
      }
    }
    .image-preview-action {
      position: absolute;
      width: 100%;
      height: 100%;
      left: 0;
      top: 0;
      cursor: default;
      text-align: center;
      color: #fff;
      opacity: 0;
      font-size: 20px;
      background-color: rgba(0, 0, 0, .5);
      transition: opacity .3s;
      cursor: pointer;
      text-align: center;
      line-height: 200px;
      .el-icon-delete {
        font-size: 36px;
      }
    }
    &:hover {
      .image-preview-action {
        opacity: 1;
      }
    }
  }
  .image-app-preview {
    width: 320px;
    height: 180px;
    position: relative;
    border: 1px dashed #d9d9d9;
    float: left;
    margin-left: 50px;
    .app-fake-conver {
      height: 44px;
      position: absolute;
      width: 100%; // background: rgba(0, 0, 0, .1);
      text-align: center;
      line-height: 64px;
      color: #fff;
    }
  }
}
</style>

修改api/douban-book.js

封装上传图片方法和新增图片方法,这两个方法都是post请求。

export function uploadDouBanBookImageAPI(data) {
  return request({
    url: '/douban/book/upload/',
    method: 'post',
    data
  })
}


export function createDouBanBookAPI(data) {
  return request({
    url: '/douban/book/',
    method: 'post',
    data
  })
}

修改router/index.js

在router/index.js中,创建二级路由,二级路由下有两个页面,/douban/book对应的是图书展示页面,/douban/create对应的是图书新增页面。

 {
    path: '/douban',
    component: Layout,
    name: 'douban',
    meta: { title: '豆瓣图书', icon: 'dashboard' },
    children: [
      {
        path: 'book',
        component: () => import('@/views/book/index'),
        name: 'book',
        meta: { title: '图书列表', icon: 'form', affix: true }
      },
      {
        path: 'create',
        component: () => import('@/views/book/create'),
        name: 'create',
        meta: { title: '图书创建', icon: 'form', affix: true }
      }
    ]
  },

测试

我们在前端上传一部《新大学英语》,填写完字段,上传封面。

图片

创建成功后,可以看到已经跳转到图书列表页,并且在图书列表的最后一条信息就是我们刚刚创建的。

图片

打包

前后端分离开发完成后,最终要把项目的代码合在一起。因此需要对前端项目进行打包,前端打包后需要在后端做相应的配置,这样最终才能使得外部通过后端服务来访问图书系统。

前端打包

1.打包前将.env.production中的VUE_APP_BASE_API改为http://192.168.1.13:8000(192.168.1.13是我的ip,你的可能不一样),注意不要使用127.0.0.1:8000,否则同一个局域网的其他用户会无法访问后端服务。

图片

2.使用命令npm run build:prod或者在前端服务里做相应的配置,在Scripts选项下选择build:prod,最后点击Apply。

图片

3.打包完成后,前端项目下会生成一个dist目录,将这个目录全部拷贝到后端项目下。

图片

后端配置

1.在settings.py中配置模板文件和静态文件的地址,并且将ALLOWED_HOSTS修改为*,允许所有ip访问。

 ALLOWED_HOSTS = ['*']

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [ os.path.join(BASE_DIR, 'dist') ],
    }
]

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'dist/static'),
]

2.在父路由urls.py中配置html的路由。

 urlpatterns = [
    path('', TemplateView.as_view(template_name='index.html')),
]

3.配置后端服务的Host为0.0.0.0,Port为8000,这样局域网的其他用户才能访问。

图片

访问后端服务

在同一个局域网的其他电脑的浏览器中输入后端服务地址:192.168.1.13:8000,发现可以正常访问。

图片

总结

不知不觉,这个系列已经写了八篇文章,在这些文章里我们从搭建环境做起,到解决登录问题,然后依次实现了图书信息的CURD(增删改查),最后完成了打包部署,就如前文说的,掌握了这些基本的方法,你可以扩展更多更丰富的功能出来。

也许有人会说,测试学这些干什么。当然,这些东西在工作中可能用不到,但却可以成为面试找工作的“敲门砖”。

在如今越来越卷的时代,多学一点知识,不要只局限于测试,也许某一天,你觉得开发有意思,走开发的路子未尝不是一条新路,也许你刚好碰上vue + django的项目架构,你的这些知识无疑对于了解项目大有裨益,也许你还可以从"点点点"升级为自动化测试,甚至测试开发工程师,从而实现升职加薪。

但这一切的前提是,你要比别人更有竞争力。还等什么,积极行动起来吧!当写完几个功能之后,你会发现web开发的妙处,当某个“疑难杂症”的问题突然迎刃而解之时,你会得到超乎寻常的快乐。


资源分享

下方这份完整的软件测试视频学习教程已经上传CSDN官方认证的二维码,朋友们如果需要可以自行免费领取 【保证100%免费】

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于TMS320F28335的应用开发是一种数字信号处理(DSP)的实践过程,它将帮助开发者学会如何使用TMS320F28335这一DSP芯片进行系统设计和开发。以下将手把手为你介绍学习DSP的步骤。 首先,需要了解TMS320F28335芯片的基本特性和功能。该芯片具有高速运算、丰富的接口和内存资源,支持多种外设和通信协议。详细查阅相关资料,包括用户手册和开发工具的文档,了解它的架构、寄存器功能和编程模型。 接下来,配置开发环境。下载并安装适合TMS320F28335的集成开发环境(IDE),例如Code Composer Studio。通过IDE连接开发板与计算机,并确保通信正确。 然后,学习DSP的基本概念和算法。了解数字信号处理的基础理论知识,例如采样定理、滤波器设计和离散傅里叶变换等。深入研究常用的DSP算法,如卷积、快速傅里叶变换(FFT)和滤波器等。 开始编程和应用开发。使用C语言或汇编语言编写程序,实现各种DSP算法和功能。利用DSP芯片的强大计算能力,开发音频处理、图像识别、机器学习等应用。同时,进行调试和性能优化,确保程序的正确性和高效性。 与此同时,学习硬件接口和外设的使用。芯片的外设包括模数转换器(ADC)、数模转换器(DAC)、通信接口等。掌握数据输入输出的方法,了解使用外设与其他设备进行数据传输的原理和步骤。 最后,进行系统集成和测试。将开发好的DSP应用与其他硬件或外设进行连接,构建完整的系统。进行功能验证和性能测试,调整和改进系统以满足设计要求。 通过以上步骤,你将逐步学习并掌握基于TMS320F28335的应用开发。不断实践和探索,掌握更多高级的DSP算法和技术,将帮助你在数字信号处理领域取得更大的成就。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值