介绍
接收到一个业务需求,也就是当我在写 Confluence 文档的时候,经常遇到直接复制内容到其他平台的情况。那么这个时候如果我当前的内容里面带有图片,并且页面还有权限,那么就会出现在我其他页面访问的时候,这个图片内容粘贴过去的时候就是裂图,因为没有权限可以看
先了解下 Confluence 的附件设计
Confluence 的附件有一个数字用来定义属性:文件自己的内容 id 和 文件所在页面中的内容 id 。这个意思是文件在逻辑上是属于内容的,通常内容又是属于空间(不是所有的内容都属于空间)。Confluence 中的空间文件,目录结构通常有 8 个级别,每一个目录级别的名字通常基于下面的算法。
开始撸代码
逻辑图
前置条件
在计算路径前,需要知道一下信息
页面 PageID
空间的 SpaceID
附件的 ContentID
附件路径计算方法
func confluencePath(pageId string, spaceId string, contentId string) string {
attachmentPath := "ver003"
for i := len(spaceId); i >= 0; i -= 3 {
start := i - 3
if start < 0 {
start = 0
}
substr := spaceId[start:i]
result := modulo(substr, 250)
attachmentPath = attachmentPath + "/" + fmt.Sprintf("%d", result)
if start == len(spaceId)-6 {
attachmentPath = attachmentPath + "/" + spaceId
break
}
}
for i := len(pageId); i >= 0; i -= 3 {
start := i - 3
if start < 0 {
start = 0
}
substr := pageId[start:i]
result := modulo(substr, 250)
attachmentPath = attachmentPath + "/" + fmt.Sprintf("%d", result)
if start == len(pageId)-6 {
attachmentPath = attachmentPath + "/" + pageId
break
}
}
return attachmentPath + "/" + contentId + "/1"
}
func modulo(substr string, module int) int {
num := 0
for _, ch := range substr {
num = num*10 + int(ch-'0')
num %= module
}
return num
}
处理复制出来的格式
由于每个人复制的方式不同,可能造成附件的地址不同,因此需要做成可配置项,目前已知的是这几种
pages/viewpage\.action\?pageId=(\d+)&preview=/(\d+)/(\d+)/([^&]+)
download/attachments/(\d+)/([^?]+)
download/thumbnails/(\d+)/([^?]+)
for _, value := range config.URLPatterns {
// 编译正则表达式
re := regexp.MustCompile(value.Info.Pattern)
// 匹配正则表达式
matches := re.FindStringSubmatch(urlPath)
var confluenceContent ConfluenceContent
if len(matches) > value.Info.Fields.ContentId && len(matches) > value.Info.Fields.PageId {
confluenceContent = query(value.Info.Fields.Type, matches[value.Info.Fields.ContentId], matches[value.Info.Fields.PageId])
confluenceAttachmentPath := confluencePath(confluenceContent.PAGEID, confluenceContent.SPACEID, confluenceContent.CONTENTID)
if len(confluenceAttachmentPath) < 20 {
http.Error(w, "地址不正确", http.StatusBadRequest)
return
} else {
imageRender(w, r, config.SourceBegin+confluenceAttachmentPath)
return
}
}
}
通过 ContentId 和附件名称获取计算需要的信息
func query(values ...interface{}) ConfluenceContent {
if len(values) < 2 {
return ConfluenceContent{}
}
var runSql string
if values[0] == "title" && values[1] != nil && values[2] != nil {
runSql = fmt.Sprintf("SELECT `CONTENTID`,`TITLE`,`PAGEID`,`SPACEID` FROM CONTENT WHERE `CONTENTTYPE` = 'ATTACHMENT' AND `TITLE` = '%s' AND `PAGEID` = '%s'", values[1], values[2])
}
if values[0] == "contentId" && values[1] != nil && values[2] != nil {
runSql = fmt.Sprintf("SELECT `CONTENTID`,`TITLE`,`PAGEID`,`SPACEID` FROM CONTENT WHERE `CONTENTTYPE` = 'ATTACHMENT' AND `CONTENTID` = '%s' AND `PAGEID` = '%s'", values[1], values[2])
}
if len(runSql) > 0 {
rows, err := database.DB.Query(runSql)
if err != nil {
panic(err.Error())
}
defer rows.Close()
// Iterate through the results
for rows.Next() {
var confluenceContent ConfluenceContent
if err := rows.Scan(&confluenceContent.CONTENTID, &confluenceContent.TITLE, &confluenceContent.PAGEID, &confluenceContent.SPACEID); err != nil {
panic(err.Error())
}
return confluenceContent
}
if err = rows.Err(); err != nil {
panic(err.Error())
}
}
return ConfluenceContent{}
}
完结
有了这信息,后面的就是 HTTP 的处理了,完整的案例代码在下方:
GitHub: [点击直达](https://github.com/almightyYantao/Confluence-Proxy-Image)