问题描述
我正在从以下位置获取 pdf:
return this.http.get(path,{ observe: 'response',responseType: 'arraybuffer' });
并将其作为 blob 处理:
let blob: Blob = new Blob([response.body],{ type: 'application/pdf' });
我现在希望将该文件存储在本地并通过本地 url 引用它。我需要使用 url 的原因是因为 viewer.loadModel()
需要它作为参数。我曾尝试使用 let url = window.URL.createObjectURL(this.blobToFile(blob,'viewer.pdf'));
获取网址。
当我这样做时,我得到:https://localhost:3000/35ef3b8e-e59f-4fe9-a8cc-1e2e542ffb2a
但是,如果没有 '.pdf' 后面的文件,查看者是不知道文件类型的。如果我把 .pdf 放在那个 url 后面:https://localhost:3000/35ef3b8e-e59f-4fe9-a8cc-1e2e542ffb2a.pdf
,它似乎加载了一些数据,但我收到以下错误:
core.js:6241 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'getData' of undefined
TypeError: Cannot read property 'getData' of undefined
at v (Viewer3D.js:1335)
at PDFLoader.js:1358
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:386)
at Object.onInvoke (core.js:41697)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:385)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:143)
at zone.js:891
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:41675)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:420)
at resolvePromise (zone.js:832)
at zone.js:739
at zone.js:755
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:386)
at Object.onInvoke (core.js:41697)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:385)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:143)
at zone.js:891
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:41675)
在下面的最后一行:(Viewer3D.js:1335)
Viewer3D.prototype.loadModel = async function(url,options,onSuccessCallback,onErrorCallback,onWorkerStart) {
var self = this;
// Kind of sucks,but I Couldn't think of a better way because loaders
// are so
var reservation = self.impl._reserveLoadingFile(); // Reserve a slot for a loading file
options = options || {};
if (typeof options.skipPropertyDb === "undefined") {
var skipParam = getParameterByName("skipPropertyDb") || "";
options.skipPropertyDb = skipParam === "true" ? true : (skipParam === "false" ? false : undefined);
}
var loaderInstance;
function onDone( error,model ) {
self.impl._removeLoadingFile(loaderInstance);
if (error) {
self.dispatchEvent({ type: et.LOADER_LOAD_ERROR_EVENT,error: error,loader: loaderInstance })
onError( error.code,error.msg,error.args );
return;
}
model.getData().underlayRaster = options.underlayRaster && model.getLeaflet();
有没有办法可以从 blob 本地存储 pdf,然后获取一个可以使用它的本地 URL?或者有什么方法可以传递不记名令牌以便加载程序可以访问存储?我不知道还能做什么,因为viewer.loadModel() 似乎只有在我使用文件的本地绝对路径时才有效(例如https://localhost:3000/Content/Documents/qcad1.pdf") . 我还想提一下,我无法通过模型衍生 API 处理它,因为如果我这样做,它会丢失 pdf 向量。
更多背景: 这是我加载模型的地方...
Autodesk.Viewing.Initializer({ env: 'Local' },() => {
this.viewer = new Autodesk.Viewing.GuiViewer3D(document.getElementById('forgeViewer'));
this.viewer.start();
this.viewer.setTheme('dark-theme');
this.viewer.loadExtension('Autodesk.Viewing.Markupscore');
this.viewer.loadModel(this.pdfUrl,{},this.initializeMeasureSettings.bind(this),() => {
console.log('File Could not be loaded');
// On error was hoping to load a sample model but it loses context to the viewer somehow
//this.viewer.loadModel(this.samplePDF,this.initializeMeasureSettings);
});
this.viewer.addEventListener('measurement-changed',this.onMeasurementChanged.bind(this));
this.viewer.addEventListener('measurement-completed',this.onMeasurementCompleted.bind(this));
this.viewer.addEventListener('finished-calibration',this.onCalibrationFinished.bind(this));
this.viewer.addEventListener('delete-measurement',this.onObjectDeleted.bind(this));
this.viewer.addEventListener('extensionLoaded',this.extensionLoaded.bind(this));
});
解决方法
所以我最终深入研究了 pdfLoader,我找到了一种向下传递不记名令牌的方法,PDFJS 在从 url 加载文件时使用该令牌!
就在调用 viewer.loadModel
之前,我设置了:
Autodesk.Viewing.endpoint.HTTP_REQUEST_HEADERS = { Authorization: `Bearer ${this.authenticationService.getToken}` };
这是访问 PDFLoader.js:749 params.httpHeaders = av.endpoint.HTTP_REQUEST_HEADERS;
。
这个 params 对象最终被传递到 Promise.resolve(第 1370 行)中的 PDFJS.getDocument(params)
。
这是 pdf.js 中的标准功能,但也许 Autodesk 可以通过允许它在 viewer.loadModel() 中的选项对象中传递它来使其更容易。
无论如何,这允许我从后端安全地下载文件并将它们加载到查看器中。
,恐怕 loadModel
方法不支持以任何方式指定自定义 HTTP 请求标头,因此如果对您的 PDF 文件的访问受访问令牌保护,那将会很棘手。
您可以尝试“破解” loadModel
函数内部的逻辑,不是通过将 .pdf
直接附加到数据 URI,而是通过在 URL 查询或哈希中伪造扩展名,例如:
https://localhost:3000/35ef3b8e-e59f-4fe9-a8cc-1e2e542ffb2a?name=foo.pdf
或
https://localhost:3000/35ef3b8e-e59f-4fe9-a8cc-1e2e542ffb2a#foo.pdf
这可能足以说服查看者使用 PDF 加载器,同时它不会破坏 URL 的含义。