大家好~我是米洛

我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持。

欢迎关注我的公众号米洛的测开日记,获取最新文章教程!

回顾

上一节我们编写好了oss相关的crud接口,那这一节我们就得为oss数据的管理编写一个新的页面了。

即将做的是一个极度精简的文件管理页面

效果图

因为我每次都是写完一段代码,然后编写对应教程,所以效果图这种东西自然是不在话下:

图片可以点击下载,也可以删除

编写oss下载接口

在此之前还是先搞定下之前的作业

  • 编写随机获取文件名的方法

根据传入的文件名,配合pity的打乱顺序+当前时间戳,基本可以保证百分之90的文件名冲突问题。

  • 实现阿里云的下载文件方法

调用get_object_to_file即可,并返回新的文件路径。

  • 封装下载文件的方法

  • 编写下载文件接口
@router.get("/download")
async def download_oss_file(filepath: str):
"""
更新oss文件,路径不能变化
:param filepath:
:return:
"""
try:
client = OssClient.get_oss_client()
# 切割获取文件名
filename = filepath.split("/")[-1]
path = client.download_file(filepath, filename)
return PityResponse.file(path, filename)
except Exception as e:
return PityResponse.failed(f"下载失败: {e}")

注意: 由于oss里面的文件名都是带路径的(目录),所以我们split一下取出文件名

看看下载的效果

前端页面部分

前端页面表格那块自然是传统手艺,不需要多说了。

值得注意的是下面这几个地方:

  • 上传文件表单

    上传文件表单里面有个Upload组件,这个比较特殊,我们需要手动上传,我也踩了很久的坑。

    这里附上源码:

这些几乎都是根据antd官网的例子来的,其中Form.Item套了2层,demo就是这么写的,我试了下去掉一个还不行。

valuePropName固定要是fileList

  • 上传文件的http请求

    由于前端未使用axios这样的http请求库,而是umi-request(自己封装的)。

    所以这边还是说明一下怎么使用:

这是文件上传的方法,第一是要制造FormData对象,并把文件数据append进去。

第二个关键就是requestType要指定为form,这2点我摸索了挺久。

  • 下载文件

    用window.open或者a标签链接到我们的download接口接口:

    window.open(${CONFIG.URL}/oss/download?filepath=${record.key})

  • 全部代码

import {PageContainer} from "@ant-design/pro-layout";
import {Button, Card, Col, Divider, Form, Input, Modal, Row, Table, Upload} from "antd";
import {InboxOutlined, PlusOutlined} from "@ant-design/icons";
import {CONFIG} from "@/consts/config";
import {useEffect, useState} from "react";
import {connect} from 'umi';
import moment from "moment"; const Oss = ({loading, dispatch, gconfig}) => { const [form] = Form.useForm();
const {ossFileList, searchOssFileList} = gconfig;
const [visible, setVisible] = useState(false);
const [value, setValue] = useState(''); const onDeleteFile = key => {
dispatch({
type: 'gconfig/removeOssFile',
payload: {
filepath: key
}
})
} const listFile = () => {
dispatch({
type: 'gconfig/listOssFile',
})
} const columns = [
{
title: '文件路径',
key: 'key',
dataIndex: 'key',
render: key => <a href={`${CONFIG.URL}/oss/download?filepath=${key}`} target="_blank">{key}</a>
},
{
title: '大小',
key: 'size',
dataIndex: 'size',
render: size => `${Math.round(size / 1024)}kb`
},
{
title: '更新时间',
key: 'last_modified',
dataIndex: 'last_modified',
render: lastModified => moment(lastModified * 1000).subtract(moment().utcOffset() / 60 - 8, 'hours').format('YYYY-MM-DD HH:mm:ss') },
{
title: '操作',
key: 'ops',
render: (record) => <>
<a onClick={() => {
window.open(`${CONFIG.URL}/oss/download?filepath=${record.key}`)
}}>下载</a>
<Divider type="vertical"/>
<a onClick={() => {
onDeleteFile(record.key);
}}>删除</a>
</>
},
]
const normFile = (e) => {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
}; const onUpload = async () => {
const values = await form.validateFields();
const res = dispatch({
type: 'gconfig/uploadFile',
payload: values,
})
if (res) {
setVisible(false)
setValue('')
listFile();
}
} useEffect(() => {
if (value === '') {
dispatch({
type: 'gconfig/save',
payload: {searchOssFileList: ossFileList}
})
} else {
dispatch({
type: 'gconfig/save',
payload: {searchOssFileList: ossFileList.filter(v => v.key.toLowerCase().indexOf(value.toLowerCase()) > -1)}
})
}
}, [value]) useEffect(() => {
listFile();
}, []) return (
<PageContainer title="OSS文件管理" breadcrumb={false}>
<Card>
<Modal width={600} title="上传文件" visible={visible} onCancel={() => setVisible(false)} onOk={onUpload}>
<Form form={form} {...CONFIG.LAYOUT}>
<Form.Item label="文件路径" name="filepath"
rules={[{required: true, message: '请输入文件要存储的路径, 目录用/隔开'}]}>
<Input placeholder="请输入文件要存储的路径, 目录用/隔开"/>
</Form.Item>
<Form.Item label="文件" required>
<Form.Item name="files" valuePropName="fileList" getValueFromEvent={normFile} noStyle
rules={[{required: true, message: '请至少上传一个文件'}]}>
<Upload.Dragger name="files" maxCount={1}>
<p className="ant-upload-drag-icon">
<InboxOutlined/>
</p>
<p className="ant-upload-text">点击或拖拽文件到此区域上传</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Modal>
<Row gutter={[8, 8]} style={{marginBottom: 12}}>
<Col span={6}>
<Button type="primary" onClick={() => setVisible(true)}><PlusOutlined/>添加文件</Button>
</Col>
<Col span={12}/>
<Col span={6}>
<Input placeholder="输入要查找的文件名" value={value} onChange={e => {
setValue(e.target.value);
}}/>
</Col>
</Row>
<Table rowKey={record => record.key} dataSource={searchOssFileList} columns={columns}
loading={loading.effects['gconfig/listOssFile']}/>
</Card>
</PageContainer>
)
} export default connect(({loading, gconfig}) => ({loading, gconfig}))(Oss);

今天的内容就介绍到这里,如今我们已经可以通过oss去管理我们的文件了,那我们要怎么运用呢?

下一节给大家介绍,http请求支持文件上传功能。

测试平台系列(91) 编写oss管理页面的更多相关文章

  1. 测试平台系列(90) 编写oss客户端

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持. 欢迎关注我的公众号米洛的测开日记,获取最新文章教程! 回顾 上一节我们编写了在线执行测试 ...

  2. 测试平台系列(79) 编写Redis配置功能(下)

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上一节我们提出了优化Dao ...

  3. 测试平台系列(81) 编写在线执行Redis功能

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上一节我们牛刀小试,编写了 ...

  4. 测试平台系列(80) 封装Redis客户端

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上一节我们编写了Redis ...

  5. 测试平台系列(82) 解决APScheduler重复执行的问题

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上一节我们编写了在线执行R ...

  6. 测试平台系列(83) 前置条件支持Redis语句

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上节我们打了个野,解决了一 ...

  7. 测试平台系列(92) 让http请求支持文件上传

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持. 欢迎关注我的公众号米洛的测开日记,获取最新文章教程! 回顾 上一节呢,我们编写了oss的 ...

  8. 测试平台系列(95) 前置条件支持简单的python脚本

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持. 欢迎关注我的公众号米洛的测开日记,获取最新文章教程! 回顾 上一节我们构思了一下怎么去支 ...

  9. 测试平台系列(97) 完善执行case部分

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持. 欢迎关注我的公众号米洛的测开日记,获取最新文章教程! 回顾 上一节我们讨论了怎么结束一个 ...

随机推荐

  1. C++ 类对象内存模型分析

    编译环境:Windows10 + VS2015 1.空类占用的内存空间 类占内存空间是只类实例化后占用内存空间的大小,类本身是不会占内存空间的.用sizeof计算类的大小时,实际上是计算该类实例化后对 ...

  2. uni showLoading 还有注意关闭 闭包, .finally

    uni.showLoading({ title:'正在提交...' }); let data = JSON.parse(JSON.stringify($this.sendData)); const r ...

  3. linux内存不足时,为了防止报错,可以使用swap

    1. 创建分区文件, 大小 2G dd if=/dev/zero of=/swapfile bs=1k count=2048000 2. 生成 swap 文件系统 mkswap /swapfile 3 ...

  4. K8s多节点部署+负载均衡+keepalived ——囊萤映雪

    K8s多节点部署+负载均衡+keepalived --囊萤映雪 1.多节点master2 部署 2.负载均衡部署+keepalived 1.多节点master2部署: #从master01节点上拷贝证 ...

  5. 【HDU6662】Acesrc and Travel(树型Dp)

    题目链接 大意 给出一颗树,每个点上有一个权值\(A[i]\),有两个绝顶聪明的人甲和乙. 甲乙两人一起在树上轮流走,不能走之前经过的点.(甲乙时刻在一起) 甲先手,并可以确定起点.甲想要走过的点权之 ...

  6. 敲出的第一个python程序

    学习python第二天,终于照猫画虎编辑出第一个程序.程序要求如下: 1.输入用户名.密码 2.认证成功后显示欢迎信息 3.输错三次后锁定 源代码如下: username = 'jackson'pas ...

  7. v78.01 鸿蒙内核源码分析(消息映射篇) | 剖析LiteIpc(下)进程通讯机制 | 百篇博客分析OpenHarmony源码

    百篇博客分析|本篇为:(消息映射篇) | 剖析LiteIpc(下)进程通讯机制 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析( ...

  8. Spring Boot 热插拔技术应用

    对Spring/Spring Boot使用频繁的开发者,应该常见在应用Application上加@EnableXXX类似的注解.其实这个@EnableXXX的注解就是热插拔技术,加了这个就可以启动对应 ...

  9. tip8:CentOS8安装ftp服务器

    之前习惯使用OpenSuse,其图形化的安装.现在刚开始使用CentOS,老老实实使用命令吧! 1.本地cmd命令ftp链接虚拟机无法链接.查出虚拟机ftp服务是否开启:没有 ps -ef|grep ...

  10. Spring boot 项目中put提交Date数据时出现type=Bad Request, status=400状态码

    1.问题原因 经过测试发现,当客户端页面提交日期为空时会出现以下异常,如果提交日期不为空则不会出现上述问题.出现这种错误的原因是没有对代码中的Date型参数进行格式化,接收为null的日期类型参数时, ...