• Asp.net与office web apps的整合


    事实上网上有关office web app的整合已经有相关的文章了,典型的是怎样整合Office Web Apps至自己开发的系统(一) 和怎样整合Office Web Apps至自己开发的系统(二),微软官网也有对应的demo

    这里在简单描写叙述一下原理吧:office web apps(owas)扮演者一个客服端,它会訪问我们asp.net 网站的文件然后呈现出来。而我们经常使用的API主要有例如以下3个:

    GET api/wopi/files/{name}?access_token={access_token}    
    GET api/wopi/files/{name}/contents?

    access_token={access_token}     
    POST api/wopi/files/{name}/contents?access_token={access_token}

    至于每一个API做什么 这里就不多说。第一个是owas 检查文件,传递的信息是json数据格式,第二个是owas获取文件流,第三个是owas post的文件流(保存改动文件)。首先我们来看看第一个API的实现:

     [Route("files/{name}/")]
            public CheckFileInfo GetFileInfo(string name, string access_token)
            {
                Validate(name, access_token);
                var fileInfo = _fileHelper.GetFileInfo(name);
                bool updateEnabled = false;
                if (bool.TryParse(WebConfigurationManager.AppSettings["updateEnabled"].ToString(), out updateEnabled))
                {
                    fileInfo.SupportsUpdate = updateEnabled;
                    fileInfo.UserCanWrite = updateEnabled;
                    fileInfo.SupportsLocks = updateEnabled;
                }
                return fileInfo;
            }
    这里的 Validate(name, access_token) 方法主要是验证请求的文件名称name与參数access_token是否一致。主要是验证是否是非法訪问。返回一个CheckFileInfo对象。CheckFileInfo的定义例如以下:

    public class CheckFileInfo
        {
            public CheckFileInfo()
            {
                this.SupportsUpdate = false;
                this.UserCanWrite = false;
            }
            public string BaseFileName { get; set; }
            public string OwnerId { get; set; }
            public long Size { get; set; } //in bytes
            public string SHA256 { get; set; } //SHA256: A 256 bit SHA-2-encoded [FIPS180-2] hash of the file contents
            public string Version { get; set; }  //changes when file changes.
            public bool SupportsUpdate { get; set; }
            public bool UserCanWrite { get; set; }
            public bool SupportsLocks { get; set; }
        }

    如今在来看看第二个api的实现。主要返回相应文件的数据流:

    [Route("files/{name}/contents")]
            public HttpResponseMessage Get(string name, string access_token)
            {
                try
                {
                    Validate(name, access_token);
                    var file = HostingEnvironment.MapPath("~/App_Data/" + name);
                    var responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
                    var stream = new FileStream(file, FileMode.Open, FileAccess.Read);
                    responseMessage.Content = new StreamContent(stream);
                    responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                    return responseMessage;
                }
                catch (Exception ex)
                {
                    var errorResponseMessage = new HttpResponseMessage(HttpStatusCode.InternalServerError);
                    var stream = new MemoryStream(UTF8Encoding.Default.GetBytes(ex.Message ?? ""));
                    errorResponseMessage.Content = new StreamContent(stream);
                    return errorResponseMessage;
                }
            }

    而第三个api是将返回的数据流保存到物理文件:

    [Route("files/{name}/contents")]
            public async void Post(string name, [FromUri] string access_token)
            {
                var body = await Request.Content.ReadAsByteArrayAsync();
                var appData = HostingEnvironment.MapPath("~/App_Data/");
                var fileExt = name.Substring(name.LastIndexOf('.') + 1);
                var outFile = Path.Combine(appData,name);
                File.WriteAllBytes(outFile, body);
            }

    如今我们再来看看怎样请求owas。也就是相应的url是怎么产生的。

    比如我的owas server是owas.contoso.com。那么我们在配置好owas后就能够訪问http://owas.contoso.com/hosting/discovery 如图:

     这里我们以excel为例  大家看到上面有view、edit、mobileview三个action。这里的app是一个excel。我们知道我们物理文件的后缀找到相应的app。在依据我们系统的配置採用edit还是view action,假设是pdf 我们仅仅能採用相应的view。假设请求是mobile发起的话, 那么我们仅仅能用mobileview。 找到相应的action后我们就获取到相应的urlsrc属性,这里我们实际须要的url地址是 http://owas.contoso.com/x/_layouts/xlviewerinternal.aspx这个东东。那么获取这个url的代码例如以下:

    public class LinkController : ApiController
        {
            /// <summary>
            /// Provides a link that can be used to Open a document in the relative viewer
            /// from the Office Web Apps server
            /// </summary>
            /// <param name="fileRequest">indicates the request type</param>
            /// <returns>A link usable for HREF</returns>
            public Link GetLink([FromUri] FileRequest fileRequest)
            {
                if (ModelState.IsValid)
                {
                    var xml = WebConfigurationManager.AppSettings["appDiscoveryXml"];
                    var wopiServer = WebConfigurationManager.AppSettings["appWopiServer"];
                    bool updateEnabled = false;
                    bool.TryParse(WebConfigurationManager.AppSettings["updateEnabled"], out updateEnabled);
                    WopiAppHelper wopiHelper = new WopiAppHelper(HostingEnvironment.MapPath(xml), updateEnabled);
    
                    var result = wopiHelper.GetDocumentLink(wopiServer + fileRequest.name);
    
                    var rv = new Link
                    {
                        Url = result
                    };
                    return rv;
                }
    
                throw new ApplicationException("Invalid ModelState");
            }
        }
    
    public class WopiAppHelper
        {
            string _discoveryFile;
            bool _updateEnabled = false;
            WopiHost.wopidiscovery _wopiDiscovery;
    
            public WopiAppHelper(string discoveryXml)
            {
                _discoveryFile = discoveryXml;
    
                using (StreamReader file = new StreamReader(discoveryXml))
                {
                    XmlSerializer reader = new XmlSerializer(typeof(WopiHost.wopidiscovery));
                    var wopiDiscovery = reader.Deserialize(file) as WopiHost.wopidiscovery;
                    _wopiDiscovery = wopiDiscovery;
                }
            }
    
            public WopiAppHelper(string discoveryXml, bool updateEnabled)
                : this(discoveryXml)
            {
                _updateEnabled = updateEnabled;
            }
    
            public WopiHost.wopidiscoveryNetzoneApp GetZone(string AppName)
            {
                var rv = _wopiDiscovery.netzone.app.Where(c => c.name == AppName).FirstOrDefault();
                return rv;
            }
    
            public string GetDocumentLink(string wopiHostandFile)
            {
                var fileName = wopiHostandFile.Substring(wopiHostandFile.LastIndexOf('/') + 1);
                var accessToken = GetToken(fileName);
                var fileExt = fileName.Substring(fileName.LastIndexOf('.') + 1);
                var netzoneApp = _wopiDiscovery.netzone.app.AsEnumerable()
                    .Where(c => c.action.Where(d => d.ext == fileExt).Count() > 0);
    
                var appName = netzoneApp.FirstOrDefault();
    
                if (null == appName) throw new ArgumentException("invalid file extension " + fileExt);
    
                var rv = GetDocumentLink(appName.name, fileExt, wopiHostandFile, accessToken);
    
                return rv;
            }
    
            string GetToken(string fileName)
            {
                KeyGen keyGen = new KeyGen();
                var rv = keyGen.GetHash(fileName);
    
                return HttpUtility.UrlEncode(rv);
            }
    
            const string s_WopiHostFormat = "{0}?WOPISrc={1}&access_token={2}";
            //HACK:
            const string s_WopiHostFormatPdf = "{0}?PdfMode=1&WOPISrc={1}&access_token={2}";
    
            public string GetDocumentLink(string appName, string fileExtension, string wopiHostAndFile, string accessToken)
            {
                var wopiHostUrlsafe = HttpUtility.UrlEncode(wopiHostAndFile.Replace(" ", "%20"));
                var appStuff = _wopiDiscovery.netzone.app.Where(c => c.name == appName).FirstOrDefault();
    
                if (null == appStuff)
                    throw new ApplicationException("Can't locate App: " + appName);
    
                var action = _updateEnabled ? "edit" : "view";
                if (appName.Equals("WordPdf"))
                {
                    action = "view";
                }
                if (HttpContext.Current.Request.Browser.IsMobileDevice)
                {
                    action = "mobileView";
                }
                var appAction = appStuff.action.Where(c => c.ext == fileExtension && c.name == action).FirstOrDefault();
    
                if (null == appAction)
                    throw new ApplicationException("Can't locate UrlSrc for : " + appName);
    
                var endPoint = appAction.urlsrc.IndexOf('?');
                var endAction = appAction.urlsrc.Substring(0, endPoint);
    
                string fullPath = null;
                ////HACK: for PDF now just append WordPdf option...
                if (fileExtension.Contains("pdf"))
                {
                    fullPath = string.Format( s_WopiHostFormatPdf, endAction, wopiHostUrlsafe, accessToken);
                }
                else
                {
                    fullPath = string.Format(s_WopiHostFormat, endAction,  wopiHostUrlsafe, accessToken);
                }
    
                return fullPath;
            }
        }

    对应的配置例如以下:

    appDiscoveryXml 是我们owas(http://owas.contoso.com/hosting/discovery)产生的数据文件。appWopiServer 表示我们的owas将要訪问interface地址。updateEnabled主要是表示owas能否够改动我们的文档,假设是true 我们上面的action 採用edit,为false採用view。

    appHmacKey仅仅是数据加密的一个key。生成的url如图:

    注意这里的配置是updateEnabled=true 表示owas是能够编辑文件的,如图:

    当我们点击在浏览器编辑 结果如图:

    改动后能够直接保存:

    点击确认后就能够直接保存。 pptx的编辑模式例如以下:

    这里的docx文件的编辑模式一直都在报错搞了非常久也没搞定。错误信息例如以下,假设大家知道还请指导指导:

    pdf是没有编辑模式的,如今再来看看excel的仅仅读模式(view)例如以下:

    这里的菜单中并不包括“在浏览器中编辑”。当中第15行是我刚才改动的新数据。

    docx和pptx的仅仅读模式就不贴图了,在mobile的执行结果例如以下(我这里是用android手机訪问我的网站,因为是通过wifi来訪问自己的电脑上的网站,这里须要把计算机的全名改为IP地址)。

     

    注意上面的url是192.168.1.25XXX,这里的ip是owas.contoso.com的IP。这里总结一下的測试结果例如以下:

      view edit mobileview remark
    word 通过 未通过 通过 在http和https协议下view都通过。edit view没有通过。mobileview仅仅測试了http协议
    excel 通过 通过 通过 在http和https协议下view和edit都通过,mobileview仅仅測试了http协议
    ppt 通过 通过 通过 在http和https协议下view和edit都通过,mobileview仅仅測试了http协议
    pdf 通过 不存在edit action 未通过 view在http协议下通过,在https在协议下未通过,mobileview 未通过

     这里我把问题的重心放在word的edit上面,对于pdf 在owas採用https以及在mobile上不能訪问的原因未未做调查。知道这些问题的革命前辈还请指教。

    源代码下载地址:http://download.csdn.net/detail/dz45693/7215395

    https://code.msdn.microsoft.com/office/Building-an-Office-Web-f98650d6

  • 相关阅读:
    拳击游戏(虚函数应用)
    虚函数的使用
    继承中的二义性归属问题
    继承的作用以及在子类中初始化所有数据的方法
    Exploring ES2016 Decorators
    Storage information for PWA application
    浏览器中常见网络协议介绍
    vuex所有核心概念完整解析State Getters Mutations Actions
    搭建一个webpack微服务器
    nodeJS接入微信公众平台开发
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/6933625.html
Copyright © 2020-2023  润新知