feat(web): 增加HDFS文件管理器
在页面直接查看HDFS文件和目录,还可以查看和下载
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
package com.lanyuanxiaoyao.service.web.controller;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.lanyuanxiaoyao.service.configuration.entity.PageResponse;
|
||||
import com.lanyuanxiaoyao.service.configuration.entity.hudi.HPath;
|
||||
import com.lanyuanxiaoyao.service.configuration.entity.hudi.HudiCleanerPlan;
|
||||
import com.lanyuanxiaoyao.service.configuration.entity.hudi.HudiCompactionPlan;
|
||||
import com.lanyuanxiaoyao.service.configuration.entity.hudi.HudiInstant;
|
||||
@@ -10,15 +12,21 @@ import com.lanyuanxiaoyao.service.configuration.entity.hudi.HudiRollbackPlan;
|
||||
import com.lanyuanxiaoyao.service.forest.service.HudiService;
|
||||
import com.lanyuanxiaoyao.service.web.controller.base.AmisCrudResponse;
|
||||
import com.lanyuanxiaoyao.service.web.controller.base.AmisDetailResponse;
|
||||
import com.lanyuanxiaoyao.service.web.controller.base.AmisMapResponse;
|
||||
import com.lanyuanxiaoyao.service.web.controller.base.AmisResponse;
|
||||
import com.lanyuanxiaoyao.service.web.controller.base.BaseController;
|
||||
import com.lanyuanxiaoyao.service.web.entity.HPathChildrenVO;
|
||||
import com.lanyuanxiaoyao.service.web.entity.HPathVO;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.collections.api.factory.Maps;
|
||||
import org.eclipse.collections.api.map.ImmutableMap;
|
||||
import org.eclipse.collections.api.map.MutableMap;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@@ -163,4 +171,46 @@ public class HudiController extends BaseController {
|
||||
}
|
||||
throw new Exception("Flink job id and alias or hdfs cannot be blank");
|
||||
}
|
||||
|
||||
@GetMapping("hdfs_list")
|
||||
public AmisCrudResponse hdfsList(@RequestParam("root") String root) throws Exception {
|
||||
if (StrUtil.isBlank(root)) {
|
||||
throw new Exception("Root path cannot be blank");
|
||||
}
|
||||
return AmisCrudResponse.responseCrudData(hudiService.list(root).collect(HPathVO::new));
|
||||
}
|
||||
|
||||
@GetMapping("hdfs_list_children")
|
||||
public AmisResponse<HPathChildrenVO> hdfsListChildren(@RequestParam("root") String root) throws Exception {
|
||||
if (StrUtil.isBlank(root)) {
|
||||
throw new Exception("Root path cannot be blank");
|
||||
}
|
||||
return AmisResponse.responseSuccess(new HPathChildrenVO(hudiService.list(root).collect(HPathVO::new)));
|
||||
}
|
||||
|
||||
@GetMapping("hdfs_read")
|
||||
public AmisMapResponse hdfsRead(@RequestParam("root") String root) throws Exception {
|
||||
if (StrUtil.isBlank(root)) {
|
||||
throw new Exception("Root path cannot be blank");
|
||||
}
|
||||
return AmisResponse.responseMapData()
|
||||
.setData("text", hudiService.read(root));
|
||||
}
|
||||
|
||||
@GetMapping(value = "hdfs_download")
|
||||
public void hdfsDownload(@RequestParam("root") String root, HttpServletResponse response) throws Exception {
|
||||
if (StrUtil.isBlank(root)) {
|
||||
throw new Exception("Root path cannot be blank");
|
||||
}
|
||||
HPath hPath = hudiService.get(root);
|
||||
if (hPath.getFolder()) {
|
||||
throw new Exception("Folder cannot be downloaded");
|
||||
}
|
||||
try (InputStream inputStream = hudiService.download(root)) {
|
||||
response.setHeader("Content-Type", MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader("Content-Disposition", StrUtil.format("attachment;filename={}", hPath.getName()));
|
||||
response.setHeader("Content-Length", hPath.getSize().toString());
|
||||
IoUtil.copy(inputStream, response.getOutputStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.lanyuanxiaoyao.service.web.entity;
|
||||
|
||||
import com.lanyuanxiaoyao.service.configuration.entity.hudi.HPath;
|
||||
import org.eclipse.collections.api.list.ImmutableList;
|
||||
|
||||
/**
|
||||
* HPath对象的有children版本
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @date 2024-04-26
|
||||
*/
|
||||
public class HPathChildrenVO {
|
||||
private final ImmutableList<HPathVO> children;
|
||||
|
||||
public HPathChildrenVO(ImmutableList<HPathVO> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public ImmutableList<HPathVO> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AmisCrudChildrenVO{" +
|
||||
"children=" + children +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.lanyuanxiaoyao.service.web.entity;
|
||||
|
||||
import cn.hutool.core.io.unit.DataSizeUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.lanyuanxiaoyao.service.configuration.entity.hudi.HPath;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @date 2024-04-26
|
||||
*/
|
||||
public class HPathVO {
|
||||
@JsonIgnore
|
||||
private final HPath hPath;
|
||||
|
||||
private final Boolean defer;
|
||||
private final String sizeText;
|
||||
|
||||
public HPathVO(HPath hPath) {
|
||||
this.hPath = hPath;
|
||||
this.defer = hPath.getFolder();
|
||||
this.sizeText = DataSizeUtil.format(hPath.getSize());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return hPath.getName();
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return hPath.getPath();
|
||||
}
|
||||
|
||||
public Boolean getFile() {
|
||||
return hPath.getFile();
|
||||
}
|
||||
|
||||
public Boolean getFolder() {
|
||||
return hPath.getFolder();
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return hPath.getSize();
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return hPath.getGroup();
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return hPath.getOwner();
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return hPath.getPermission();
|
||||
}
|
||||
|
||||
public Integer getReplication() {
|
||||
return hPath.getReplication();
|
||||
}
|
||||
|
||||
public Long getModifyTime() {
|
||||
return hPath.getModifyTime();
|
||||
}
|
||||
|
||||
public Boolean getDefer() {
|
||||
return defer;
|
||||
}
|
||||
|
||||
public String getSizeText() {
|
||||
return sizeText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HPathVO{" +
|
||||
"hPath=" + hPath +
|
||||
", defer=" + defer +
|
||||
", sizeText='" + sizeText + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,176 @@ function toolTab() {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'form',
|
||||
title: 'HDFS文件管理器',
|
||||
actions: [
|
||||
{
|
||||
label: '直接下载',
|
||||
type: 'action',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'custom',
|
||||
script: (context, action, event) => {
|
||||
let downloadUrl = `${event.data.base}/hudi/hdfs_download?root=${encodeURI(event.data.hdfs)}`
|
||||
window.open(downloadUrl, '_blank')
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
label: '查看',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: {
|
||||
type: 'tpl',
|
||||
tpl: '${hdfs}'
|
||||
},
|
||||
...readOnlyDialogOptions(),
|
||||
size: 'xl',
|
||||
body: {
|
||||
type: 'crud',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: '${base}/hudi/hdfs_list',
|
||||
data: {
|
||||
root: '${hdfs|default:undefined}',
|
||||
},
|
||||
},
|
||||
deferApi: {
|
||||
method: 'get',
|
||||
url: '${base}/hudi/hdfs_list_children',
|
||||
data: {
|
||||
root: '${path|default:undefined}',
|
||||
},
|
||||
},
|
||||
...crudCommonOptions(),
|
||||
perPage: 10,
|
||||
headerToolbar: [
|
||||
"reload",
|
||||
paginationCommonOptions(undefined, 10),
|
||||
],
|
||||
footerToolbar: [
|
||||
paginationCommonOptions(undefined, 10),
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
name: 'name',
|
||||
label: '文件名',
|
||||
className: 'font-mono',
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
label: '组',
|
||||
width: 70,
|
||||
},
|
||||
{
|
||||
name: 'owner',
|
||||
label: '用户',
|
||||
width: 70,
|
||||
},
|
||||
{
|
||||
label: '大小',
|
||||
width: 90,
|
||||
type: 'tpl',
|
||||
tpl: "${sizeText}",
|
||||
},
|
||||
{
|
||||
label: '修改时间',
|
||||
width: 140,
|
||||
type: 'tpl',
|
||||
tpl: "${DATETOSTR(DATE(modifyTime), 'YYYY-MM-DD HH:mm:ss')}",
|
||||
},
|
||||
{
|
||||
type: 'operation',
|
||||
label: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
buttons: [
|
||||
{
|
||||
label: '完整路径',
|
||||
type: 'action',
|
||||
level: 'link',
|
||||
size: 'xs',
|
||||
actionType: 'copy',
|
||||
content: '${path}',
|
||||
tooltip: '复制 ${path}',
|
||||
},
|
||||
{
|
||||
disabledOn: 'folder || size > 1048576',
|
||||
label: '查看',
|
||||
type: 'action',
|
||||
level: 'link',
|
||||
size: 'xs',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: {
|
||||
type: 'tpl',
|
||||
tpl: '文件内容:${path}'
|
||||
},
|
||||
size: 'md',
|
||||
...readOnlyDialogOptions(),
|
||||
body: {
|
||||
type: 'service',
|
||||
api: {
|
||||
method: 'get',
|
||||
url: '${base}/hudi/hdfs_read',
|
||||
data: {
|
||||
root: '${path|default:undefined}'
|
||||
}
|
||||
},
|
||||
body: {
|
||||
type: 'textarea',
|
||||
name: 'text',
|
||||
readOnly: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
disabledOn: 'folder',
|
||||
label: '下载',
|
||||
type: 'action',
|
||||
level: 'link',
|
||||
size: 'xs',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'custom',
|
||||
script: (context, action, event) => {
|
||||
let downloadUrl = `${event.data.base}/hudi/hdfs_download?root=${encodeURI(event.data.path)}`
|
||||
window.open(downloadUrl, '_blank')
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'hdfs',
|
||||
label: 'HDFS根路经',
|
||||
required: true,
|
||||
clearable: true,
|
||||
description: '输入表HDFS路径',
|
||||
autoComplete: '${base}/table/all_hdfs?key=$term',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'form',
|
||||
title: '查询时间线',
|
||||
|
||||
Reference in New Issue
Block a user