JohnToStr 2019-06-30
Autodesk Forge API服务的数据中心是基于AWS的海外服务搭建的,因而,由于众所周知的原因,国内部分地区(依ISP而异)访问Forge云端口的速度会受到一定程度的影响。特别是Forge Viewer浏览大型模型,以及对反馈时间比较铭感且涉及关键业务的工作流等诸多场景,对于服务端的存取效率有者较高要求。所以,如何处理好云端数据的访问与协同工作流,包括离线加载、云端缓存等方案的最佳实践,是大家一直关注的问题。今天,我们就总结一下几种常见的实现方式,以期实现流程与性能的优化。
往期有这两篇文章可供参考:离线模型的下载和部署和Viewer模型加载本地离线缓存实战,该文介绍了使用新近浏览器原生的Service-Worker和Cache API缓存模型的方案。
但是在Viewer模型加载本地离线缓存实战的实例代码中,针对Viewer库和线上模型资源本身的缓存是通过静态路径实现的:
//https://github.com/petrbroz/forge-disconnected/blob/master/public/service-worker.js const STATIC_URLS = [ 'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/style.css', 'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/viewer3D.js', 'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/lmvworker.js', 'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/res/locales/en/allstrings.json', 'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/res/environments/SharpHighlights_irr.logluv.dds', 'https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/res/environments/SharpHighlights_mipdrop.logluv.dds', ...
该实现存在几点问题:
因此,在我们后续的实战Forge Viewer渐进应用一文中,采用先注册完成缓存任务的Service Worker再加载Viewer库的流程。如此一来,Viewer库依赖与模型资源的加载请求也会自动得到缓存,无需手动干预缓存过程,大幅增进代码的可维护性:
navigator.serviceWorker.register('/service-worker.js').then((registration) => { let script = document.createElement('script'); script.onload = function () { const viewer = new Autodesk.Viewing.Private.GuiViewer3D(myViewerDiv); Autodesk.Viewing.Initializer(options, () => { ... //按需以在线或离线模式初始化Viewer并加载模型 viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, () => { const channel = new MessageChannel(); channel.port1.onmessage = (event) => console.log(event); navigator.serviceWorker.controller.postMessage({ operation: 'EXECUTE_CACHE' }, [channel.port2]); // 模型加载完成,该模型所需资源已作记录,遂向ServiceWorker发送消息,开始缓存所需资源 }) }); }; script.src = "https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/viewer3D.min.js"; document.head.appendChild(script) //待Worker注册完毕开始作动后再载入Viewer,实现Viewer库及其依赖的自动缓存 })
Autodesk.Viewing.endpoint.setEndpointAndApi
来重定向获取模型数据的URL:Autodesk.Viewing.Initializer(options, function(){ Autodesk.Viewing.endpoint.setEndpointAndApi('https://yourhostname/your/proxy/service/path') ...
随后在你的服务端将Viewer发来的请求代理至Forge API云服务https://developer.api.autodesk.com
即可,注意保留Viewer原生请求的路径,这里还可以根据Forge服务上的模型所在的数据中心按需为转发目的地作进一步设置,详见:BIM 360 Docs API在操作欧洲数据中心内容的一些调整。
Autodesk.Viewing.Initializer(options, function(){ Autodesk.Viewing.endpoint.HTTP_REQUEST_HEADERS = {'X-My-Custom-Header':'233', ...} ...
当然,我们还需在自己的服务端设置预检请求(Pre-flight)的返回中Access-Control-Allow-Headers
的请求头,允许之前为Viewer设置的请求头不被浏览器的跨域安全机制拦截。
File
协议的模型加载,需要通过重写WebView并拦截http请求,返回静态资源。webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("http://my/path/to/svf")) { // 返回静态模型资源 return true; } } });
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) { if navigationAction.navigationType == .linkActivated { if webView.url!.absoluteString == "http://path/to/my/static/svf" { //返回静态模型资源 return } } decisionHandler(.allow) }
针对跨平台的框架,不建议使用搭设本地静态服务器的方式克服Viewer不支持File
协议的问题,因为很多Android ROM处于安全考量已经屏蔽了该特性。
onShouldStartLoadWithRequest
拦截并返回静态模型:onShouldStartLoadWithRequest = (event) => { var url = event.url; if (url && url.length) { if (url.indexOf('http://my/path/to/svf') == 0) { //返回静态模型资源 } } return true; };
Ionic框架
onShouldStartLoadWithRequest
拦截并返回静态模型cdvfile://path/to/svf
Piggyback
的方式,即在自己的逻辑中动态的替换、扩展Viewer自身的函数