想要使用OWA需要一臺單獨的伺服器來部署,這對很多人造成困難。而寫該文的目的是為了分享有個OWA的集成步驟,它不僅適用於.Net開發環境,其它語言也是一樣的,只要實現了需要的服務介面。並且該文不局限與OWA的研究,還包括Config、XML、Cache、Redis等技術。更重要的是熟悉.Net Co ...
想要使用OWA需要一臺單獨的伺服器來部署,這對很多人造成困難。而寫該文的目的是為了分享有個OWA的集成步驟,它不僅適用於.Net開發環境,其它語言也是一樣的,只要實現了需要的服務介面。並且該文不局限與OWA的研究,還包括Config、XML、Cache、Redis等技術。更重要的是熟悉.Net Core開發。
在OwaFileInfo的SupportsUpdate、UserCanWrite、SupportsLocks屬性為true時將允許用戶線上編輯文件。
在原有的基礎上編輯一個test.xlsx文件,其訪問鏈接為(測試鏈接沒有實際意義):http://owa.test.com/x/_layouts/xlviewerinternal.aspx?edit=1&WOPISrc=http%3a%2f%2f192.168.1.1%2fapi%2fwopi%2ffiles%2ftest.xlsx&access_token=H7lFBYT4pVMK
將彈出如下對話框
點擊編輯副本
查看請求日誌發現,另存副本請求將調用PutRelativeFile服務,所有我們應該再原來的基礎上添加PutRelativeFile服務。
PutRelativeFile服務
PutRelativeFile操作將在主機上創建一個基於當前文件的副本文件,創建成功後必須將副本文件名及URL以Json的格式返回給OWA伺服器。
如果OWA客戶端設置了CheckFileInfo的SupportsUpdate為true的話,就必須要實現PutRelativeFile服務,否則需要設置UserCanNotWriteRelative為true並且返回501狀態碼。
Method:POST
URI:HTTP://server/<...>/wopi*/files/<id>
Request Headers:
X-WOPI-Override 固定值 PUT_RELATIVE
X-WOPI-SuggestedTarget 文件擴展名或全名(可修改)
X-WOPI-RelativeTarget 文件全名(不可更改)
X-WOPI-OverwriteRelativeTarget 是否覆蓋文件名
X-WOPI-Size 文件的大小bytes
X-WOPI-FileConversion 指示請求是否轉換文件
Response Headers:
X-WOPI-ValidRelativeTarget
X-WOPI-Lock
X-WOPI-LockFailureReason
該介面需要返回的Json格式如下:
{Name:”test.xlsx”,Url:” http://server/<...>/wopi/files/ test.xlsx”, HostViewUrl :””, HostEditUrl :””}
說明:Name為新的文件名(如果修改的話),Url為新的文件的CheckFileInfo服務路徑。
線上Excel用到這個服務的情況有兩種:
1.另存為,如果沒有實現PutRelativeFile服務,則另存為將不能使用;
2.當編輯伺服器不支持的Excel某些格式的文件時,會先保存一個系統支持格式的副本,如果沒有實現PutRelativeFile服務,則不能編輯該Excel(因為保存新文件會失敗);
PutRelativeFile服務與CheckFileInfo服務的URL是相同的,只是Method不同,所以我們只需在CheckFileInfo的基礎上修改。
[RouteAttribute("files/{name}")] public JsonResult GetFileInfo(string name, string access_token) { string wopiType = Request.Headers["X-WOPI-Override"]; Console.WriteLine("1.X-WOPI-Override:" + wopiType); if (wopiType == "PUT_RELATIVE") { using (FileStream fs = System.IO.File.Create("Files/new_" + name)) { Request.Body.CopyTo(fs); } Response.Headers.Add("X-WOPI-ValidRelativeTarget", "new_" + name); Response.Headers.Add("X-WOPI-Lock", Request.Headers["X-WOPI-Lock"]); Response.StatusCode = 200; PutRelativeFile file = new PutRelativeFile(); file.Name = "new_" + name; file.Url = "http://b1wcfoqm7r.proxy.qqbrowser.cc/api/wopi/files/new_" + name; return Json(file); } else if (wopiType == "UNLOCK" || wopiType == "LOCK") { Response.Headers.Add("X-WOPI-Lock", Request.Headers["X-WOPI-Lock"]); Response.StatusCode = 200; return Json(""); } else { FileHelper helper = new FileHelper(); var info = helper.GetFileInfo(name); info.SupportsUpdate = true; info.SupportsLocks = true; info.UserCanWrite = true; return Json(info); }GetFileInfo
[DataContract(Name = "PutRelativeFile")] public class PutRelativeFile { [DataMember(Name = "Name")] public string Name { get; set; } [DataMember(Name = "Url")] public string Url { get; set; } [DataMember(Name = "HostViewUrl")] public string HostViewUrl { get; set; } [DataMember(Name = "HostEditUrl")] public string HostEditUrl { get; set; } }PutRelativeFile
Discovery.xml
訪問OWA伺服器的如下地址:http://owa.host.com/hosting/discovery(該鏈接無實際意義)就能得到OWA伺服器能夠處理的所有文件格式、操作及相關的URL。
<action name="view" ext="xls" default="true" urlsrc="http://owa.hand-china.com/x/_layouts/xlviewerinternal.aspx?<ui=UI_LLCC&><rs=DC_LLCC&>"/>
name=”view”表示動作為查看,ext=”xls”表示文件擴展名為xls,urlsrc則表示相應動作的地址。
以預覽test.xls為例,其ChecFileInfo路徑為:http://localhost:5000/api/wopi/files/test.xls
則最終預覽的鏈接為:http://owa.host.com/x/_layouts/xlviewerinternal.aspx?WOPISrc=http://localhost:5000/api/wopi/files/test.xls
我將OWA服務提交的discovery內容放到了本地的xml文件中,讀取方法如下:
public static List<DiscoveryAction> GetDiscoveryAction() { List<DiscoveryAction> list = new List<DiscoveryAction>(); XmlReader reader = XmlReader.Create("Files/Discovery.xml"); //迴圈Read方法直到文檔結束 while (reader.Read()) { //Console.WriteLine("rdr.NodeType = " + reader.NodeType); //如果是開始節點 if (reader.NodeType == XmlNodeType.Element) { //通過rdr.Name得到節點名 string elementName = reader.Name; //讀取到cat元素 這時rdr.Read()讀取到的內容為<cat color="white"> if (elementName == "action") { DiscoveryAction act = new DiscoveryAction(); //可以通過中括弧獲得屬性值 act.Name = reader["name"]; act.UrlSrc = reader["urlsrc"]; act.Default = TransBool(reader["default"]); act.Ext = reader["ext"]; act.ProGid = reader["progid"]; act.Requires = reader["requires"]; list.Add(act); } } } return list; }GetDiscovery
Discovery.xml文件下載:Disconvery
Controller Action代碼如下:
[Route("files/{name}/links")] [HttpGetAttribute] public JsonResult GetOwaLinks(string name, string type, string access_token, string lang) { if (string.IsNullOrEmpty(type)) { type = "view"; } if (string.IsNullOrEmpty(lang)) { lang = "zh-CN"; } if (string.IsNullOrEmpty(access_token)) { access_token = "H7lFBYT4pVMK"; } List<string> lst = new List<string>(); var res = XmlHelper.GetDiscoveryAction(); var ext = Path.GetExtension(name).TrimStart('.'); var discovery = res.Where(t => t.Ext == ext && t.Name == type); foreach (var item in discovery) { Console.WriteLine(item.UrlSrc); var index = item.UrlSrc.IndexOf('<'); var api = System.Net.WebUtility.UrlEncode("http://localhost:5000/api/wopi/files/" + name); lst.Add(string.Format(item.UrlSrc.Substring(0, index) + "WOPISrc={0}&access_token={1}&ui={2}", api, access_token, lang)); } return Json(lst); }GetOwaLinks
測試訪問:http://localhost:5000/api/wopi/files/test.docx/links