文件属性字段如何扩展?
该组件仅适用于私有化WPS365版本
1. 方案概述
区分文件类别的信息就是文件属性,比如:.docx指文档,.xlsx指表格。
这里说的拓展文件属性指的是在文件属性后面打标签,方便后续查找和管理文件。
有哪些标签可以添加
企业可以根据自己的需求自行打标签,常用的标签有三类:
安全管控:比如标记“机密文件”,限制未经授权的人查看或编辑。
流程跟踪:比如标注“待审批”状态,提醒相关负责人处理。
协作管理:比如显示“可/不可分享”状态,避免文件外泄。
针对这种需求,我们可以使用文档中心Web端(PC端)的开放能力,定制文件属性扩展:
| 实现效果 | 在.docx后,增添文件的更多属性,红色字体可根据企业需要,替换成企业的分类标签。![]() |
| 支持端 | 文档中心PC端 |
| 版本限制 | 版本23-12b(含)以上 |
| 操作系统限制 | 支持X86、XC |
2. 使用指南
文件属性字段扩展
支持文件列表显示更多信息,如:分享状态.审批状态.安全标记等
👉能力
sdk.kdrive.myDocumentPage.fileList
👉代码示例
sdk.kdrive.myDocumentPage.fileList.onUpdate((data) => {
const dataList = data.list.items()
const fileList = sdk.kdrive.myDocumentPage.fileList
const slotName = 'fileName'
dataList.length &&
dataList.forEach((item) => {
const slot = fileList.fileItem(item['id'])[slotName]
sdk.append(slot, <div style={{ color: 'red' }}>demo示例</div>, {
key: 'test-tag',
})
})
})
👉效果截图

文件属性如何写到元数据
- 第一步:注册实体扩展信息
post /config/dev/v1/reg_entity_extra
- 第二步:修改文件元数据
post /v7/dev/drives/{drive_id}/files/{file_id}/update
- 第三步:获取文件信息
get /v7/drives/{drive_id}/files/{file_id}/meta
👉效果截图
服务端
注册实体扩展信息
type Api struct {
domain string
ak string
sk string
driveId string
FileId string
}
var (
api *Api
apiSrvOnce sync.Once
)
func NewApiSrv() *Api {
apiSrvOnce.Do(func() {
api = &Api{
domain: "http://encs-pri-cams-engine",
ak: config.GlobalConfig.AK,
sk: config.GlobalConfig.Sk,
}
})
return api
}
// 使用方自定义命名空间和name
var namespace, name string = "filetransfer", "transferTag"
// 注册实体信息扩展接口
func (a *Api) RegisterEntityExt() error {
entityExtInfo := &EntityExtReq{
Copyable: true,
EntityType: "file",
Movable: true,
Name: name,
Namespace: namespace,
RecycleRestorable: true,
VersionRestorable: true,
}
h, _ := json.Marshal(entityExtInfo)
uri := "/config/dev/v1/reg_entity_extra"
// i接口,wps4签名
wps4Header, err := wps4DocSign.Wps4DocsSign(a.ak, a.sk, uri, "POST", h)
if err != nil {
log.Error("wps4DocSign.Wps4DocsSign error: ", err)
return fmt.Errorf("wps4DocSign.Wps4DocsSign error: %s", err)
}
// 发送请求
url := fmt.Sprintf("%s/i/docmini%s", a.domain, uri)
request, err := http.NewRequest("POST", url, bytes.NewReader(h))
if err != nil {
log.Error("http.NewRequest error: ", err)
return fmt.Errorf("http.NewRequest error: %s", err)
}
request.Header.Set("Content-Type", wps4Header.ContentType)
request.Header.Set("Wps-Docs-Date", wps4Header.Date)
request.Header.Set("Wps-Docs-Authorization", wps4Header.Authorization)
client := &http.Client{}
resp, err := client.Do(request)
if err != nil {
log.Error("client.Do error: ", err)
return fmt.Errorf("client.Do error: %s", err)
}
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
}
var respBody []byte
if resp.StatusCode != http.StatusOK {
if resp.Body != nil {
respBody, _ = io.ReadAll(resp.Body)
log.Error("RegisterEntityExt error: ", string(respBody), resp.StatusCode)
return fmt.Errorf("RegisterEntityExt error: %s", string(respBody))
} else {
log.Error("RegisterEntityExt error: ", resp.StatusCode)
return fmt.Errorf("RegisterEntityExt error: %d", resp.StatusCode)
}
}
respBody, _ = io.ReadAll(resp.Body)
respInfo := &BaseResp{}
err = json.Unmarshal(respBody, respInfo)
if err != nil {
log.Error("json.Unmarshal error: ", err)
return fmt.Errorf("json.Unmarshal error: %s", err)
}
log.Info("RegisterEntityExt success: ", respInfo)
return nil
}
修改文件元数据
// 修改文件元数据接口
func (a *Api) ModifyFileMeta(driveId, fileId string) error {
fileMetaReq := &ModifyFileMetaReq{
ExtAttr: ExtAttr{
Name: name,
Value: "test",
},
}
h, _ := json.Marshal(fileMetaReq)
uri := fmt.Sprintf("/v7/dev/drives/%s/files/%s/update", driveId, fileId)
// i接口,wps4签名
wps4Header, err := wps4DocSign.Wps4DocsSign(a.ak, a.sk, uri, "POST", h)
if err != nil {
log.Error("wps4DocSign.Wps4DocsSign error: ", err)
return fmt.Errorf("wps4DocSign.Wps4DocsSign error: %s", err)
}
// 发送请求
url := fmt.Sprintf("%s/i/docmini%s", a.domain, uri)
request, err := http.NewRequest("POST", url, bytes.NewReader(h))
if err != nil {
log.Error("http.NewRequest error: ", err)
return fmt.Errorf("http.NewRequest error: %s", err)
}
request.Header.Set("Content-Type", wps4Header.ContentType)
request.Header.Set("Wps-Docs-Date", wps4Header.Date)
request.Header.Set("Wps-Docs-Authorization", wps4Header.Authorization)
client := &http.Client{}
resp, err := client.Do(request)
if err != nil {
log.Error("client.Do error: ", err)
return fmt.Errorf("client.Do error: %s", err)
}
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
}
var respBody []byte
if resp.StatusCode != http.StatusOK {
if resp.Body != nil {
respBody, _ = io.ReadAll(resp.Body)
log.Error("ModifyFileMeta error: ", string(respBody), resp.StatusCode)
return fmt.Errorf("ModifyFileMeta error: %s", string(respBody))
} else {
log.Error("ModifyFileMeta error: ", resp.StatusCode)
return fmt.Errorf("ModifyFileMeta error: %d", resp.StatusCode)
}
}
respBody, _ = io.ReadAll(resp.Body)
respInfo := &BaseResp{}
err = json.Unmarshal(respBody, respInfo)
if err != nil {
log.Error("json.Unmarshal error: ", err)
return fmt.Errorf("json.Unmarshal error: %s", err)
}
log.Info("ModifyFileMeta success: ", respInfo)
return nil
}
获取文件信息
// 获取文件信息接口
func (a *Api) GetFileInfo(driveId, fileId string) (*FileInfoResp, error) {
uri := fmt.Sprintf("/open/v7/drives/%s/files/%s/meta?with_permission=true&with_ext_attrs=true", driveId, fileId)
wps4Header, err := wps4DocSign.Wps4DocsSign(a.ak, a.sk, uri, "GET", nil)
if err != nil {
log.Error("wps4DocSign.Wps4DocsSign error: ", err)
return nil, fmt.Errorf("wps4DocSign.Wps4DocsSign error: %s", err)
}
// 发送请求
url := fmt.Sprintf("%s/i/docmini%s", a.domain, uri)
request, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Error("http.NewRequest error: ", err)
return nil, fmt.Errorf("http.NewRequest error: %s", err)
}
request.Header.Set("Content-Type", wps4Header.ContentType)
request.Header.Set("Wps-Docs-Date", wps4Header.Date)
request.Header.Set("Wps-Docs-Authorization", wps4Header.Authorization)
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
resp, err := client.Do(request)
if err != nil {
log.Error("client.Do error: ", err)
return nil, fmt.Errorf("client.Do error: %s", err)
}
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
}
var respBody []byte
if resp.StatusCode != http.StatusOK {
if resp.Body != nil {
respBody, _ = io.ReadAll(resp.Body)
log.Error("GetFileInfo error: ", string(respBody), resp.StatusCode)
return nil, fmt.Errorf("GetFileInfo error: %s", string(respBody))
} else {
log.Error("GetFileInfo error: ", resp.StatusCode)
return nil, fmt.Errorf("GetFileInfo error: %d", resp.StatusCode)
}
}
respBody, _ = io.ReadAll(resp.Body)
log.Info("GetFileInfo success: ", string(respBody))
fileInfoResp := &FileInfoResp{}
err = json.Unmarshal(respBody, fileInfoResp)
if err != nil {
log.Error("json.Unmarshal error: ", err)
return nil, fmt.Errorf("json.Unmarshal error: %s", err)
}
return fileInfoResp, nil
}
结果

前端
sdk.kdrive.myDocumentPage.fileList.onUpdate(async data => {
let tag = "transferTag"
let isRenderTag: boolean = false // 保证满足条件再进行tag渲染
let driveId = '4'
let fileId = '286034168381441'
try {
let { data: fileInfo } = await request.get(
`/cloud/file/u/v1/drives/${driveId}/files/${fileId}`
)
console.log('ecis: index start2', fileInfo)
const extraAttrs = JSON.parse(fileInfo.data.extra)
if (extraAttrs && extraAttrs[tag]) {
isRenderTag = true
}
} catch (error) {
console.error('render tag error:', error)
}
const dataList = data.list.items()
const fileList = sdk.kdrive.myDocumentPage.fileList
const slotName = 'fileName'
dataList.length && isRenderTag &&
dataList.forEach(item => {
const slot = fileList.fileItem(item['id'])[slotName]
sdk.append(slot, <div style={{ color: 'red' }}>{extraAttrs[tag]}</div>, {
key: tag
})
})
})
结果

