0%

React+GO 搭建文件服务器(前端部分)

安装React框架以及相关库

安装相关库

1
npm install react

安装react工具

1
npm install g react-create-app

工程初始化

采用TypeScript开发

使用react工具创建TS模板工程

1
npx create-react-app xxx --template typescript

引入UI库 Ant

引入现成的UI库 Ant 减少工作量

1
npm install antd --save

Ant组件使用

文件上传输入框

采用Ant现成的组件Dragger

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
render(): React.ReactNode {
return <div style={{ 'marginLeft': "5%", "marginRight": "5%" }}>
<Dragger
name='file'
multiple={true}
action={API.upload}
headers={{ "Access-Control-Allow-Origin": "*" }}
onChange={(info) => {
const { status } = info.file;
console.log("onChange", info)
if (status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (status === 'done') {
message.success(`${info.file.name} file uploaded successfully.`);
Actions.UpdateFileList(this.props.updateFileList)
} else if (status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}}
onDrop={(e) => {
console.log('Dropped files', e.dataTransfer.files);
}}
>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">点击或将文件拖动到此处进行上传</p>
<p className="ant-upload-hint">
支持单次或批量上传
</p>
</Dragger>
</div >
}
文件列表

采用Ant现成的组件List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
render(): React.ReactNode {
return <div style={{ 'marginLeft': "5%", "marginRight": "5%" }}>
<List
className='MyList'
itemLayout='vertical'
size='default'
split={true}
dataSource={this.props.data}
pagination={this.getPaginationConfig()}
renderItem={this.renderItem.bind(this)}
>
</List>
</div>
}

引入Redux框架

文件页面需要从server拉取文件列表以刷新界面,考虑到会动态刷界面,引入Redux框架管理数据

1
2
3
npm install redux
npm install react-redux
npm install @reduxjs/toolkit
  • 注意:新版Redux框架的Reducer使用方式可以参考toolkit

Redux使用示例

Action 生成

Action 类似发起的一个事件,由Reducer接受,并接受Aciton携带的数据。将收到的数据更新进store根数据中,此时使用对应数据的界面也会随之刷新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 以从服务器拉取文件列表为例
* 1. 发起requst,从服务器拉取到文件列表的json信息
* 2. 收到json信息后,用dispatch方法将这个json发送出去
*/
static UpdateFileList(dispatch: Function) {
API.json(API.fileList, (result: any, error: any) => {
if (error != null) {
console.log("UpdateFileList Error:", error)
return
}
if (!(result instanceof Array)) {
console.log("UpdateFileList Error:", result)
return
}
dispatch(result)
})
}

Action 发起与使用,和dispatch方法的使用,都需要先在组件中绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 绑定store中的数据至界面中
// 随后组件中便可以以 this.props.data 的方式使用数据
// 且当store中的data数据刷新时,相关页面也会随之刷新
const mapStateToProps = (state: any) => {
return {
data: state.fileList.items,
};
};

// 绑定dispatch方法,用来发起事件
// 当调用 this.props.updateFileList(data) 时,便会刷新store中的数据
const mapDispatchToProps = (dispatch: any) => {
return {
updateFileList: (data: any) => dispatch(update(data))
};
};

export default connect(mapStateToProps, mapDispatchToProps)(FileList)

List组件加载完成后,发起Action事件,从服务器拉文件列表

1
2
3
componentDidMount() {
Actions.UpdateFileList(this.props.updateFileList)
}
Reducer 接受数据

借助toolkit中的方法快速生成一个reducer

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
// 生成一个名为fileList的子数据结构
// 该数据结构下仅含一个名为items的列表,用来存放从服务器拉取的文件信息
// reducers.update方法,声明了items这个数据的更新方式:即收到数据后,按时间排序,然后直接赋值给items
export const FileListSlice = createSlice({
name: "fileList",
initialState: {
items: [],
},
reducers: {
update: (state, action) => {
action.payload.sort((a: FileItem, b: FileItem) => {
let ta = new Date(a.update)
let tb = new Date(b.update)
return tb.getTime() - ta.getTime()
})
state.items = action.payload
},
}
})

// 导出reducers.update方法,这个方法专门用来更新items数据
export const {
update,
} = FileListSlice.actions

export default FileListSlice.reducer
store 主数据

每个react工程,仅有一个store数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export const store = configureStore({
reducer: {
fileList: FileListSlice,
}
})

function App() {
return (
<Provider store={store}>
<div className="App">
<FilePage></FilePage>
</div>
</Provider>
);
}

文件的上传与下载

文件上传

文件上传工程由Ant自带的Dragger组件实现,设置正确的上传地址即可

1
2
3
4
5
6
7
8
9
10
11
render(): React.ReactNode {
return <Dragger
// ......
action={API.upload}
// ......
>
{
// ......
}
</Dragger>
}
文件下载

文件下载借助html的 标签即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 发起下载请求时,构造一个</a>标签,然后设置下载url
// 注意:将href设置为url,会将下载流程转交给浏览器
// 此时浏览器会接管整个下载流程,无需再实现下载进度条和文件分块等逻辑
static DownloadFile(data: any, callback: Function) {
const fileName = data.name;
const linkNode = document.createElement('a');
linkNode.download = fileName; //a标签的download属性规定下载文件的名称
linkNode.style.display = 'none';
linkNode.href = `${API.download}?md5=${data.md5}`
document.body.appendChild(linkNode);
linkNode.click(); //模拟在按钮上的一次鼠标单击
URL.revokeObjectURL(linkNode.href); // 释放URL 对象
document.body.removeChild(linkNode);
}

发布工程

1
npm run build