大家好~我是米洛

我正在从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. SharePoint Online 触发 Outlook 邮件内审批

    前言 我们在做SharePoint Online项目时, 经常会有客户问,我们能否在通知邮件中快速完成审批,而不是需要在邮件中打开系统,然后在系统中审批? 答案肯定是可以的,来!安排! 正文 1.我们 ...

  2. 使用OpenFileDialog打开文件和使用FolderBrowserDialog选定文件夹

    选定文件夹 string foldPath = ""; FolderBrowserDialog dialog = new FolderBrowserDialog(); dialog ...

  3. WebSocket协议详解及应用

    WebSocket协议详解及应用(七)-WebSocket协议关闭帧 本篇介绍WebSocket协议的关闭帧,包括客户端及服务器如何发送并处理关闭帧.关闭帧错误码及错误处理方法.本篇内容主要翻译自RF ...

  4. Info.plist常见的设置

    建立一个工程后,会在Supporting files文件夹下看到一个"工程名-Info.plist"的文件,该文件对工程做一些运行期的配置,非常重要,不能删除 在旧版本Xcode创 ...

  5. Java中的常用的输入输出流

    Java中的输入输出流根据格式又可以分为字节流和字符流:(成对) 字节流:FileInputStream,FileOutputStream   :  BufferedInputStream,Buffe ...

  6. TCP三次握手和四次挥手【转】

    一. TCP/IP协议族 TCP/IP是一个协议族,通常分不同层次进行开发,每个层次负责不同的通信功能.包含以下四个层次: 1. 链路层,也称作数据链路层或者网络接口层,通常包括操作系统中的设备驱动程 ...

  7. 转载_最值得阅读学习的10个C语言开源项目代码

    "源代码面前,了无秘密",阅读优秀代码无疑是开发人员得以窥见软件堂奥而登堂入室的捷径.本文选取10个C语言优秀开源项目的代码作为范本,分别给予点评,免去东搜西罗之苦,点赞!那么问题 ...

  8. iOS应用启动main函数

    #import <UIKit/UIKit.h> #import "AppDelegate.h" int main(int argc, char * argv[]) { ...

  9. udp的第一个例子

    import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import j ...

  10. iOS开发笔记,简单实现视频音频的边下边播

    总是在网上看别人的博文,受益匪浅,从没自己写过,今天分享一点小经验,第一次写技术博客,希望对同行的ios猿们有所帮助.   先介绍一下自己的情况:一年ios开发,http协议不太懂,大约知道断点下载h ...