前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用beego进行onlyoffice document server的二次开发

用beego进行onlyoffice document server的二次开发

作者头像
hotqin888
发布2018-09-11 15:13:33
1.6K0
发布2018-09-11 15:13:33
举报
文章被收录于专栏:hotqin888的专栏

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1334543

请移步至此——更加详细:http://blog.csdn.net/hotqin888/article/details/79337881

这个onlyoffce为实时文档协作,在局域网中某台电脑上安装服务,就可以实现任何电脑上同时打开word,excel,ppt的同时编辑,实在是方便办公协作。

商业版是收费的,所以自己搭建他们的开源版,可以免费使用。

服务器的搭建见上面一篇文章,这里讲如何用beego进行二次开发,实现文档的管理。

看它的api,解决几个问题:

当最后一个打开的浏览器标签页关闭后,会触发saver文档,此时,要求beego实现下载服务器上最新的文档,把它存到你自己的服务端;

打开文档需要唯一一个key,所有人打开这个文档,这个key是唯一的,用beego服务器上存储的文档的更新时间纳秒作为key吧。

返回一个callback,

c.Data"json" = mapstringinterface{}{"error": 0} 

c.ServeJSON()

controllers:

代码语言:javascript
复制
package controllers

import (
	"encoding/json"
	"engineercms/models"
	"github.com/astaxie/beego"
	// "github.com/astaxie/beego/utils/pagination"
	"io"
	"net/http"
	// "github.com/astaxie/beego/httplib"
	"bytes"
	"io/ioutil"
	"mime/multipart"
	"net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type OnlyController struct {
	beego.Controller
}

type Callback struct {
	Key         string   `json:"key"`
	Status      int      `json:"status"`
	Url         string   `json:"url"`
	Changesurl  string   `json:"changesurl"`
	History     history  `json:"history"`
	Users       []string `json:"users"`
	Actions     []action `json:"actions"`
	Lastsave    string   `json:"lastsave"`
	Notmodified bool     `json:"notmodified"`
}

type action struct {
	Type   int
	userid string
}

type history struct {
	changes       []change
	serverVersion string
}

type change struct {
	created string
	User    User1
}

type User1 struct {
	id   string
	name string
}

type Onlyoffice1 struct {
	Id      int64
	Code    string
	Title   string
	Ext     string
	Created time.Time
	Updated time.Time
}

type OnlyLink struct {
	Id       int64
	Code     string
	Title    string
	Uid      int64
	Created  time.Time
	Updated  time.Time
	Docxlink []DocxLink
	Xlsxlink []XlsxLink
	Pptxlink []PptxLink
}

type DocxLink struct {
	Id    int64
	Title string
	// Link    string
	Created time.Time
	Updated time.Time
}

type XlsxLink struct {
	Id    int64
	Title string
	// Link    string
	Created time.Time
	Updated time.Time
}

type PptxLink struct {
	Id    int64
	Title string
	// Link    string
	Created time.Time
	Updated time.Time
}

func (c *OnlyController) Get() {
	// beego.Info(c.Ctx.Input.UserAgent())
	u := c.Ctx.Input.UserAgent()
	// re := regexp.MustCompile("Trident")
	// loc := re.FindStringIndex(u)
	// loc[0] > 1
	c.Data["IsOnlyOffice"] = true
	matched, err := regexp.MatchString("AppleWebKit.*Mobile.*", u)
	if err != nil {
		beego.Error(err)
	}
	if matched == true {
		beego.Info("移动端~")
		c.TplName = "onlyoffice/docs.tpl"
	} else {
		beego.Info("电脑端!")
		c.TplName = "onlyoffice/docs.tpl"
	}
	// var u = navigator.userAgent, app = navigator.appVersion;
	//       return {
	//           trident: u.indexOf('Trident') > -1, //IE内核
	//           presto: u.indexOf('Presto') > -1, //opera内核
	//           webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
	//           gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//火狐内核
	//           mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
	//           ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端//两个感叹号的作用就在于,如果明确设置了变量的值(非null/undifined/0/”“等值),结果就会根据变量的实际值来返回,如果没有设置,结果就会返回false。
	//           android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android终端
	//           iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
	//           iPad: u.indexOf('iPad') > -1, //是否iPad
	//           webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
	//           //webApp: ("standalone" in window.navigator)&&window.navigator.standalone, //是否web应该程序,没有头部与底部
	//           weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
	//           qq: u.match(/\sQQ/i) == " qq" //是否QQ

	// public static boolean  isMobileDevice(String requestHeader){
	/**
	 * android : 所有android设备
	 * mac os : iphone ipad
	 * windows phone:Nokia等windows系统的手机
	 */
	// String[] deviceArray = new String[]{"android","mac os","windows phone"};
	// if(requestHeader == null)
	//     return false;
	// requestHeader = requestHeader.toLowerCase();
	// for(int i=0;i<deviceArray.length;i++){
	//     if(requestHeader.indexOf(deviceArray[i])>0){
	//         return true;
	//     }
	// }
	// return false;

	// }

	//         Enumeration   typestr = request.getHeaderNames();
	// String s1 = request.getHeader("user-agent");
	// if(s1.contains("Android")) {
	// System.out.println("Android移动客户端");
	// } else if(s1.contains("iPhone")) {
	// System.out.println("iPhone移动客户端");
	// }  else if(s1.contains("iPad")) {
	// System.out.println("iPad客户端");
	// }  else {
	// System.out.println("其他客户端");
	// }
	// 	if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
	//   	//alert(navigator.userAgent);
	//   	window.location.href ="iPhone.html";
	// } else if (/(Android)/i.test(navigator.userAgent)) {
	//     //alert(navigator.userAgent);
	//     window.location.href ="Android.html";
	// } else {
	//     window.location.href ="pc.html";
	// };
}

//提供给列表页的table中json数据
//取得某个侧栏id下的成果给table
func (c *OnlyController) GetData() {
	var err error
	docs, err := models.GetDocs()
	if err != nil {
		beego.Error(err)
	}

	link := make([]OnlyLink, 0)
	Docxslice := make([]DocxLink, 0)
	Xlsxslice := make([]XlsxLink, 0)
	Pptxslice := make([]PptxLink, 0)
	for _, w := range docs {
		Attachments, err := models.GetOnlyAttachments(w.Id)
		if err != nil {
			beego.Error(err)
		}
		linkarr := make([]OnlyLink, 1)
		linkarr[0].Id = w.Id
		linkarr[0].Code = w.Code
		linkarr[0].Title = w.Title
		linkarr[0].Uid = w.Uid
		linkarr[0].Created = w.Created
		linkarr[0].Updated = w.Updated
		for _, v := range Attachments {
			// beego.Info(v.FileName)
			// fileext := path.Ext(v.FileName)
			if path.Ext(v.FileName) == ".docx" || path.Ext(v.FileName) == ".DOCX" {
				docxarr := make([]DocxLink, 1)
				docxarr[0].Id = v.Id
				docxarr[0].Title = v.FileName
				Docxslice = append(Docxslice, docxarr...)
			} else if path.Ext(v.FileName) == ".XLSX" || path.Ext(v.FileName) == ".xlsx" {
				xlsxarr := make([]XlsxLink, 1)
				xlsxarr[0].Id = v.Id
				xlsxarr[0].Title = v.FileName
				// pdfarr[0].Link = Url
				Xlsxslice = append(Xlsxslice, xlsxarr...)
			} else if path.Ext(v.FileName) == ".pptx" || path.Ext(v.FileName) == ".PPTX" {
				pptxarr := make([]PptxLink, 1)
				pptxarr[0].Id = v.Id
				pptxarr[0].Title = v.FileName
				// pdfarr[0].Link = Url
				Pptxslice = append(Pptxslice, pptxarr...)
			}
		}
		linkarr[0].Docxlink = Docxslice
		linkarr[0].Xlsxlink = Xlsxslice
		linkarr[0].Pptxlink = Pptxslice
		Docxslice = make([]DocxLink, 0) //再把slice置0
		Xlsxslice = make([]XlsxLink, 0) //再把slice置0
		Pptxslice = make([]PptxLink, 0)

		link = append(link, linkarr...)
	}
	c.Data["json"] = link //products
	c.ServeJSON()
}

func (c *OnlyController) OnlyOffice() {
	id := c.Ctx.Input.Param(":id")
	//pid转成64为
	idNum, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		beego.Error(err)
	}
	//根据附件id取得附件的prodid,路径
	onlyattachment, err := models.GetOnlyAttachbyId(idNum)
	if err != nil {
		beego.Error(err)
	}
	c.Data["Doc"] = onlyattachment

	c.Data["Key"] = strconv.FormatInt(onlyattachment.Updated.UnixNano(), 10)
	if path.Ext(onlyattachment.FileName) == ".docx" || path.Ext(onlyattachment.FileName) == ".DOCX" {
		c.Data["fileType"] = "docx"
		c.Data["documentType"] = "text"
	} else if path.Ext(onlyattachment.FileName) == ".XLSX" || path.Ext(onlyattachment.FileName) == ".xlsx" {
		c.Data["fileType"] = "xlsx"
		c.Data["documentType"] = "spreadsheet"
	} else if path.Ext(onlyattachment.FileName) == ".pptx" || path.Ext(onlyattachment.FileName) == ".PPTX" {
		c.Data["fileType"] = "pptx"
		c.Data["documentType"] = "presentation"
	} else if path.Ext(onlyattachment.FileName) == ".doc" || path.Ext(onlyattachment.FileName) == ".DOC" {
		c.Data["fileType"] = "doc"
		c.Data["documentType"] = "text"
	} else if path.Ext(onlyattachment.FileName) == ".txt" || path.Ext(onlyattachment.FileName) == ".TXT" {
		c.Data["fileType"] = "txt"
		c.Data["documentType"] = "text"
	} else if path.Ext(onlyattachment.FileName) == ".XLS" || path.Ext(onlyattachment.FileName) == ".xls" {
		c.Data["fileType"] = "xls"
		c.Data["documentType"] = "spreadsheet"
	} else if path.Ext(onlyattachment.FileName) == ".csv" || path.Ext(onlyattachment.FileName) == ".CSV" {
		c.Data["fileType"] = "csv"
		c.Data["documentType"] = "spreadsheet"
	} else if path.Ext(onlyattachment.FileName) == ".ppt" || path.Ext(onlyattachment.FileName) == ".PPT" {
		c.Data["fileType"] = "ppt"
		c.Data["documentType"] = "presentation"
	}

	u := c.Ctx.Input.UserAgent()
	matched, err := regexp.MatchString("AppleWebKit.*Mobile.*", u)
	if err != nil {
		beego.Error(err)
	}
	if matched == true {
		beego.Info("移动端~")
		c.TplName = "onlyoffice/onlyoffice.tpl"
	} else {
		beego.Info("电脑端!")
		c.TplName = "onlyoffice/onlyoffice.tpl"
	}
}

func (c *OnlyController) UrltoCallback() {
	// pk1 := c.Ctx.Input.RequestBody
	// beego.Info(pk1)
	// beego.Info(c.Ctx.Input.IP()) //docker的ip192.168.99.100
	id := c.Input().Get("id")
	//pid转成64为
	idNum, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		beego.Error(err)
	}
	//根据附件id取得附件的prodid,路径
	onlyattachment, err := models.GetOnlyAttachbyId(idNum)
	if err != nil {
		beego.Error(err)
	}

	var callback Callback
	json.Unmarshal(c.Ctx.Input.RequestBody, &callback)
	// beego.Info(callback.Status)
	// beego.Info(callback.Url)
	if callback.Status == 1 || callback.Status == 4 {
		c.Data["json"] = map[string]interface{}{"error": 0} //"/attachment/wiki/2018February/1517488773267674800.docx"
		c.ServeJSON()
	} else if callback.Status == 2 {
		// if callback.Status == 2 {
		resp, err := http.Get(callback.Url)
		body, err := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()
		// beego.Info(body)
		// req := httplib.Get("http://192.168.99.100:9000/cache/files/E7FAFC9C22A8_5080/output.docx/output.docx?md5=-fOxyXjPpAyxS_QZdt5jBw==&expires=1520306521&disposition=attachment&ooname=output.docx")
		// str, err := req.Bytes() //req.String()
		if err != nil {
			beego.Error(err)
		}
		f, err := os.OpenFile("tt", os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) //可读写,追加的方式打开(或创建文件)
		// f, err := os.Create("http://192.168.99.1/attachment/wiki/2018February/1517719231055607500.docx")
		if err != nil {
			beego.Error(err)
		}
		defer f.Close()
		_, err = f.Write(body)
		// _, err = f.WriteString(str)
		// _, err = io.Copy(body, f)
		if err != nil {
			beego.Error(err)
		}

		bodyBuf := &bytes.Buffer{}
		bodyWriter := multipart.NewWriter(bodyBuf)
		//关键的一步操作
		fileWriter, err := bodyWriter.CreateFormFile("uploadfile", onlyattachment.FileName)
		if err != nil {
			beego.Error(err)
		}
		//打开文件句柄操作
		fh, err := os.Open("tt")
		if err != nil {
			beego.Error(err)
		}
		defer fh.Close()

		//iocopy
		_, err = io.Copy(fileWriter, fh)
		if err != nil {
			beego.Error(err)
		}

		contentType := bodyWriter.FormDataContentType()
		bodyWriter.Close()

		resp1, err := http.Post("http://192.168.99.1/onlyoffice/post?id="+id, contentType, bodyBuf)
		if err != nil {
			beego.Error(err)
		}
		defer resp1.Body.Close()

		// err = c.SaveToFile("tt", "\\attachment\\wiki\\2018February\\1.doc")
		// if err != nil {
		// 	beego.Error(err)
		// }

		// resp_body, err := ioutil.ReadAll(resp1.Body)
		// if err != nil {
		// 	beego.Error(err)
		// }
		// beego.Info(resp1.Status)
		// beego.Info(string(resp_body))
		// fmt.Println(resp1.Status)
		// fmt.Println(string(resp_body))
		// return nil
		c.Data["json"] = map[string]interface{}{"error": 0} //"/attachment/wiki/2018February/1517488773267674800.docx"
		c.ServeJSON()
	}

	// // 创建表单文件
	//    // CreateFormFile 用来创建表单,第一个参数是字段名,第二个参数是文件名
	//    buf := new(bytes.Buffer)
	//    writer := multipart.NewWriter(buf)
	//    formFile, err := writer.CreateFormFile("uploadfile", "test.jpg")
	//    if err != nil {
	//        log.Fatalf("Create form file failed: %s\n", err)
	//    }

	//    // 从文件读取数据,写入表单
	//    srcFile, err := os.Open("test.jpg")
	//    if err != nil {
	//        log.Fatalf("%Open source file failed: s\n", err)
	//    }
	//    defer srcFile.Close()
	//    _, err = io.Copy(formFile, srcFile)
	//    if err != nil {
	//        log.Fatalf("Write to form file falied: %s\n", err)
	//    }

	//    // 发送表单
	//    contentType := writer.FormDataContentType()
	//    writer.Close() // 发送之前必须调用Close()以写入结尾行
	//    _, err = http.Post("http://localhost:9090/upload", contentType, buf)
	//    if err != nil {
	//        log.Fatalf("Post failed: %s\n", err)
	//    }
}

func (c *OnlyController) PostOnlyoffice() {
	id := c.Input().Get("id")
	//pid转成64为
	idNum, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		beego.Error(err)
	}
	//根据附件id取得附件的prodid,路径
	// onlyattachment, err := models.GetOnlyAttachbyId(idNum)
	// if err != nil {
	// 	beego.Error(err)
	// }
	//获取上传的文件
	_, h, err := c.GetFile("uploadfile")
	if err != nil {
		beego.Error(err)
	}
	if h != nil {
		//存入文件夹
		err = c.SaveToFile("uploadfile", ".\\attachment\\onlyoffice\\"+h.Filename)
		if err != nil {
			beego.Error(err)
		} else {
			err = models.UpdateOnlyAttachment(idNum)
			if err != nil {
				beego.Error(err)
			}
		}
	}
}

//向某个侧栏id下添加成果——用于第一种批量添加一对一模式
func (c *OnlyController) AddOnlyAttachment() {
	//取得客户端用户名
	v := c.GetSession("uname")
	var user models.User
	var err error
	if v != nil {
		uname := v.(string)
		user, err = models.GetUserByUsername(uname)
		if err != nil {
			beego.Error(err)
		}
	}

	var attachment string
	var filepath, DiskDirectory, Url string

	err = os.MkdirAll(".\\attachment\\onlyoffice\\", 0777) //..代表本当前exe文件目录的上级,.表示当前目录,没有.表示盘的根目录
	if err != nil {
		beego.Error(err)
	}
	DiskDirectory = ".\\attachment\\onlyoffice\\"
	Url = "/attachment/onlyoffice/"

	//获取上传的文件
	_, h, err := c.GetFile("file")
	if err != nil {
		beego.Error(err)
	}
	if h != nil {
		//保存附件
		attachment = h.Filename
		//将附件的编号和名称写入数据库
		_, filename1, filename2, _, _, _, _ := Record(attachment)
		// filename1, filename2 := SubStrings(attachment)
		//当2个文件都取不到filename1的时候,数据库里的tnumber的唯一性检查出错。
		if filename1 == "" {
			filename1 = filename2 //如果编号为空,则用文件名代替,否则多个编号为空导致存入数据库唯一性检查错误
		}
		code := filename1
		title := filename2
		//存入成果数据库
		//如果编号重复,则不写入,只返回Id值。
		//根据id添加成果code, title, label, principal, content string, projectid int64
		prodId, err := models.AddDoc(code, title, user.Id)
		if err != nil {
			beego.Error(err)
		}
		//改名,替换文件名中的#和斜杠
		filename2 = strings.Replace(filename2, "#", "号", -1)
		filename2 = strings.Replace(filename2, "/", "-", -1)
		FileSuffix := path.Ext(h.Filename)

		filepath = DiskDirectory + "\\" + filename1 + filename2 + FileSuffix
		//把成果id作为附件的parentid,把附件的名称等信息存入附件数据库
		//如果附件名称相同,则覆盖上传,但数据库不追加
		attachmentname := filename1 + filename2 + FileSuffix
		_, err = models.AddOnlyAttachment(attachmentname, 0, 0, prodId)
		if err != nil {
			beego.Error(err)
		} else {
			//存入文件夹
			err = c.SaveToFile("file", filepath) //.Join("attachment", attachment)) //存文件    WaterMark(filepath)    //给文件加水印
			if err != nil {
				beego.Error(err)
			}
			c.Data["json"] = map[string]interface{}{"state": "SUCCESS", "title": attachment, "original": attachment, "url": Url + "/" + attachment}
			c.ServeJSON()
		}
	}
}

//采用绝对路径型式
func (c *AttachController) DownloadDoc() {
	// c.Data["IsLogin"] = checkAccount(c.Ctx)
	//4.取得客户端用户名
	// var uname, useridstring string
	// v := c.GetSession("uname")
	// if v != nil {
	// 	uname = v.(string)
	// 	c.Data["Uname"] = v.(string)
	// 	user, err := models.GetUserByUsername(uname)
	// 	if err != nil {
	// 		beego.Error(err)
	// 	}
	// 	useridstring = strconv.FormatInt(user.Id, 10)
	// }

	//1.url处理中文字符路径,[1:]截掉路径前面的/斜杠
	// filePath := path.Base(ctx.Request.RequestURI)
	filePath, err := url.QueryUnescape(c.Ctx.Request.RequestURI[1:]) //  attachment/SL2016测试添加成果/A/FB/1/Your First Meteor Application.pdf
	if err != nil {
		beego.Error(err)
	}

	// fileext := path.Ext(filePath)
	//根据路由path.Dir——再转成数组strings.Split——查出项目id——加上名称——查出下级id
	// beego.Info(path.Dir(filePath))
	// filepath1 := path.Dir(filePath)
	// array := strings.Split(filepath1, "/")
	// beego.Info(strings.Split(filepath1, "/"))
	http.ServeFile(c.Ctx.ResponseWriter, c.Ctx.Request, filePath)
}

views:

代码语言:javascript
复制
<!DOCTYPE html>
<html style="height: 100%;">
<head>
    <title>fei-OnlyOffice</title>
</head>
<body style="height: 100%; margin: 0;">

    <div id="placeholder" style="height: 100%"></div>
    <script type="text/javascript" src="http://192.168.99.100:9000/web-apps/apps/api/documents/api.js"></script>

    <script type="text/javascript">
        // alert({{.Doc.FileName}});
        window.docEditor = new DocsAPI.DocEditor("placeholder",
            {
                "document": {
                    "fileType": "{{.fileType}}",
                    "key": "{{.Key}}",//"Khirz6zTPdfd7"
                    "title": "{{.Doc.FileName}}",
                    "url": "http://192.168.99.1/attachment/onlyoffice/{{.Doc.FileName}}"//"http://112.74.42.44:8086/attachment/wiki/2018February/1517489516810999500.docx"

                },
                "documentType": "{{.documentType}}",
                "editorConfig": {
                    "callbackUrl": "http://192.168.99.1/url-to-callback?id={{.Doc.Id}}",
                },
                "height": "100%",
                "width": "100%"
            });

    </script>
</body>
</html>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年02月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档