测试开发实战[提测平台]17-Flask&Vue文件上传实现

先回顾下在此系列第8次分享给出的预期实现的产品原型和需求说明,如下图整体上和前两节实现很相似,只不过一般测试报告要写的内容可能比较多,就多用了些多行输入框组件,另外一个特别的全新功能操作就是 附件上传,这是需要先解决和要掌握的重点内容。

 如果你想学习测试开发,我这边给你推荐一套视频,这个视频可以说是B站播放全网第一的测试开发教程,同时在线人数到达1000人,并且还有笔记可以领取及各路大神技术交流:798478386   

阿里巴巴P8级Python测试开发大佬,手把手教你如何独立搭建测试平台开发实战!学会这个根本不担心找不到高薪工作_哔哩哔哩_bilibili阿里巴巴P8级Python测试开发大佬,手把手教你如何独立搭建测试平台开发实战!学会这个根本不担心找不到高薪工作共计12条视频,包括:1. 为么什‬要pytest高插阶‬件定开制‬发、2. 如何搞定pytest插的件‬开发线上‬与发布、3. pytest框高架‬阶用之法‬插件制机‬及定制思路等,UP主更多精彩视频,请关注UP账号。https://www.bilibili.com/video/BV1Lk4y1J7yq/?spm_id_from=333.337.search-card.all.click&vd_source=8b9b7a6bd9e1d50c8c5643945488eade

后端服务实现附件的保存,要写个上传接口,服务端通过request.files进行获取实现,Postman模拟请求的话,方法使用POST,文件通过form-data格式中的file进行上传,一个基本的实现代码接口如下:

  1. 定义请求方法和路径

  2. 拼接一个项目保存文件夹的一个绝对路径

  3. 获取form-data指定key的文件,通过save保存后返回成功消息 

@test_manager.route("/api/report/upload",methods=['POST'])

def uploadFile():

    # 保存文件的路径

    save_path = os.path.join(os.path.abspath(os.path.dirname(__file__)).split('TPMService')[0], 'TPMService/static')

    # 获取文件

    attfile = request.files.get('file')

    attfile.save(os.path.join(save_path, attfile.filename))

    return {"code":200, "message":"上传请求成功"}

 

这里对于文件上传,一般来说不能无限制上传,需要对格式、大小做一些限制,还要做一些安全的处理,方式是通过FileField做要求如格式的限制,用secure_filename做文件名安全处理

优化后完整的代码分两个片段

1. 引入依赖和做fileForm类

import os
# 涉及的相关依赖引用
from wtforms import Form,FileField
from flask_wtf.file import FileRequired,FileAllowed
from werkzeug.utils import secure_filename
from werkzeug.datastructures import CombinedMultiDict
 
# 表单提交相关校验
class fileForm(Form):
    file = FileField(validators=[FileRequired(), FileAllowed(['jpg', 'png', 'gif', 'pdf', 'zip'])])

2. 增加格式校验和安全校验,另外这里需要注意下,我拿的直接是上传文件的名字,我并没有对文件名做一个随机生成处理,这样如果有重名文件再次上传会被覆盖掉,一般作为一个静态资源或者文件服务来说是要做生成唯一码名称,python可以使用uuid,大家可以尝试扩展下,如果是生成自己的串码名还带来另外一个问题,真的是统一文件多次反复上传如何处理,那可能就要做真正的数据文件信息存储,然后做MD5校验,由于我们只是做个简单附件服务,就不再做更多上传服务的讨论了。

@test_manager.route("/api/report/upload",methods=['POST'])
def uploadFile():
    # 初始化返回对象
    resp_success = format.resp_format_success
    resp_failed = format.resp_format_failed
 
    file_form = fileForm(CombinedMultiDict([request.form, request.files]))
    if file_form.validate():
        # 获取项目路径+保存文件夹,组成服务保存绝对路径
        save_path = os.path.join(os.path.abspath(os.path.dirname(__file__)).split('TPMService')[0], 'TPMService/static')
        # 通过表单提交的form-data获取选择上传的文件
        attfile = request.files.get('file')
        # 进行安全名称检查处理
        file_name = secure_filename(attfile.filename)
        # 保存文件文件中
        attfile.save(os.path.join(save_path, file_name))
 
        resp_success['data'] = {"fileName": file_name}
        return resp_success
    else:
        resp_failed['message'] = '文件格式不符合预期'
        return resp_failed

上边只是实现了文件格式的校验,对于上传限制大小从网上搜的资料来看,flask一般通过全局配置,比如下边是配置限制一个16MB大小的文件限制,如果超过会返回 413 Request Entity Too Large,网上资料说16M也是默认大小,但实际我测试了下,如果不设置全局限制,我传300+M除了慢点也能上传成功,并且搜索了源码 MAX_CONTENT_LENGTH = None,可能是由于版本的原因,现在没有这个限制了。 

from flask import Flask, Requestapp = Flask(__name__)app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000

关于Flask文件上传更多的解释和例子请参考 [链接1],还有一种第三方插件也可以对文件进行友好的操作参考 [链接2],不过这两种方式都是全局控制的,如果想不同的接口单独控制大小,目前尝试的方式是读取文件然后获取长度 len(attfile.read()) 其实就是字节大小,对其进行比较返回即可,如果你有更好的方案,记得告诉我。

上传文件接口搞定了,自然少不了下载接口,这个比较简单,通过flask提供的send_from_directory方法实现,代码如下,详细解释参考 [链接1] 后半部分。 

from flask import send_from_directory
 
@test_manager.route("/api/file/download",methods=['GET'])
def downloadFile():
    fimeName = request.args.get('name')
 
    # 保存文件的相对路径
    save_path = os.path.join(os.path.abspath(os.path.dirname(__file__)).split('TPMService')[0], 'TPMService/static')
 
    result = send_from_directory(save_path, fimeName)
 
    return  result
 

重启后端服务后,用postman请求做个上传测试,效果如图,一开始是个超大提示,后来正常上传返回结果成功。

刷新查看代码服务存储位置,文件已经正确上传

 下载的测试可以通过浏览GET请求服务+路径 /api/file/download?=文件名 进行下载验证。

 前端Vue的实现,用到的组件是“Upload上传”,官网给出了多种样式和方式,比如多文件,头像上传,拖拽上传,列表形式等等,具体可参考 [链接3]。

如图其中action就是上传地址,这块就可以替换成刚刚实现的上传接口 http://127.0.0.1:5000/api/report/upload 表示上传地址,默认为选择文件后自动上传,其实就是帮助你实现postman演示的表单文件自动提交,可以通过:auto-upload="false" 设置关闭,也可以通过 http-request 覆盖默认的上传行为自定义实现。

这两种方式都会实际写个Demo实践下

1. 自动上传 新建一个文件上传页面,路由绑定到跟目录,编写<template>和<script>部分代码,这里在方法中用 :on-success 钩子打印下上传成功的返回信息

<template>
  <div class="app-container">
    <el-form>
      <el-form-item label="附件" prop="test_file">
        <el-upload
          :limit="1"
          :file-list="fileList"
          :auto-upload="true"
          action="http://127.0.0.1:5000/api/report/upload"
          :on-success="uploadFile"
        >
          <el-button size="small" type="primary">点击上传</el-button>
          <div slot="tip" class="el-upload__tip">只能上传jpg/png/zip/pdf文件,且不超过1M</div>
        </el-upload>
      </el-form-item>
    </el-form>
  </div>
</template>
 
<script>
export default {
  name: 'DemoUpload',
  data() {
    return {
      fileList: []
    }
  },
  methods: {
    uploadFile(response, file, fileList) {
      console.log(response)
      console.log(file)
      console.log(fileList)
    }
  }
}
</script>

启动前后端,选择一个小于10M的文件进行上传测试,可以看到正常返回2000,并且能正常拿到钩子中三个参数信息,后边报告功能实现其实就是拿到返回的文件名赋值给一个变量即可。

再测试一种情况,文件格式不符合要求,大小超出服务端限制,发现在文件不符合格式的情况是40000,但文件列表还是显示了,做个优化处理,在状态码不正确的情况,清空filelist

还有另外一个问题就是服务端大小超限制的时候回返回403,但element vue 及upload 直接在返回的时候时候拦截处理了,所以没办法精细异常处理,就进行了模糊提示处理。优化后及 增加了 :on-success 使用的方式的代码如下:

 

<template>
  <div class="app-container">
    <el-form>
      <el-form-item label="实现一" prop="test_file">
        <el-upload
          ref="fileOne"
          :limit="1"
          :file-list="fileList"
          :auto-upload="true"
          action="http://127.0.0.1:5000/api/report/upload"
          :on-success="uploadSuccess"
          :on-error="uploadErrors"
        >
          <el-button size="small" type="primary">点击上传</el-button>
          <div slot="tip" class="el-upload__tip">只能上传jpg/png/zip/pdf文件,且不超过10M</div>
        </el-upload>
      </el-form-item>
      <el-form-item label="实现二" prop="test_file">
        <el-upload
          :limit="1"
          :file-list="fileList"
          action="http://127.0.0.1:5000/api/report/upload"
          :http-request="uploadeFile"
          :on-success="uploadSuccess"
        >
          <el-button size="small" type="primary">点击上传</el-button>
          <div slot="tip" class="el-upload__tip">只能上传jpg/png/zip/pdf文件,且不超过10M</div>
        </el-upload>
      </el-form-item>
    </el-form>
  </div>
</template>
 
<script>
import axios from 'axios'
 
export default {
  name: 'DemoUpload',
  data() {
    return {
      fileList: [],
      fileNanme: ''
    }
  },
  methods: {
    uploadSuccess(response, file, fileList) {
      if (response.code === 40000) {
        this.$message({
          message: '格式不正确或者上传异常',
          type: 'warning'
        })
        this.fileList = []
      } else {
        this.$message({
          message: '上传成功',
          type: 'success'
        })
      }
    },
    uploadErrors(err, file, fileList) {
      this.$message({
        message: '大小不符合要求或服务器异常',
        type: 'warning'
      })
    },
    uploadeFile(params) {
      console.log(params.file)
      const fd = new FormData()
      fd.append('file', params.file)
      fd.append('FileName', params.file.name)
      fd.append('async', true)
      const config = {
        headers: { 'Content-Type': 'multipart/form-data' }
      }
      axios
        .post(params.action, fd, config)
        .then(res => {
          console.log(res.data)
        })
        .catch(Error => {
          this.fileList = []
          this.$message({
            message: '大小不符合要求或服务器异常',
            type: 'warning'
          })
        })
    }
  }
}
</script>

为什么有了自动上传还是要讲个自定义上传,这里有两点:

目前为止校验都依赖后端,但实际上服务端校验是一个后置校验,文件已经上传了,如果文件大或者量大会很占用IO,所以可以自定义提交进行一些前端的上传校验。

作为实践尽量为大家趟一下坑 :on-success的使用官方并没有给出例子,而要拿到组件信息经过验证是通过参数方法参数获取,如下边红色框圈出的一些重要信息。

再做一个异常的情况下的上传测试,这是由uploadErrors钩子 或者 axios catch(error)捕获实现。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值