在 Angular 中动态创建和嵌入数据 URL
Dynamically create and embed data URL in Angular
在我的Angular申请中,我有以下不变的基本条件:
- 应用程序从外部动态加载 ZIP 文件 URL。
- 在此 ZIP 文件中,有一个要提取的 HTML 文件。此 HTML 文件可能包含具有
data:image/jpeg;base64,...
类来源的图像(但没有外部链接)。
- HTML 文件必须由 Angular 应用程序在单独的浏览器选项卡中“按原样”显示
我设法使用 JSZip 等无缝地实现了 1. 和 2. 因此,我们可以假设,现在有一个 String
变量 htmlFileContent
。在特定条件下在空的 Angular 选项卡中显示 iFrame 也不是问题,我设法做到了这一点。为了实现第 3 点的其余部分,我看到了两种不同的方法:
使用 div:
使用 htmlFileContent
作为 div
元素的 innerHTML
,像这样:
<div [innerHtml]="{{ htmlFileContent }}"></div>
这确实有效,但有一些副作用,例如。 G。 “内部”HTML header 的 title
也在浏览器中呈现。因此,我可以尝试将 htmlFileContent
解析为 DOM 并删除不需要的树元素。这可能是一个可行的解决方案,但不知何故我对此感觉不太好。
此外,HTML 包含一些 in-file 锚链接(<a href="#topOfPage">
样式),这些链接也不再有效,需要更正。
使用 iFrame:
我很清楚 iFrame 使用的丑陋和弃用,但是,在我看来,这似乎是解决我的问题的适当方法。所以我会使用:
<iframe [src]="fileUrl" class="fullscreenIFrame"></iframe>
这里出现了问题:可以设置 fileUrl="data:text/html;charset=utf-8,"+ htmlFileContent
。但是,Angular 会(正确地)抱怨:ERROR Error: NG0904: unsafe value used in a resource URL context (see https://g.co/ng/security#xss)
并且不会显示任何内容。所以,目前我正在尝试使用来自 @angular/platform-browser
:
的 DomSanitizer
this.filedata = this.sanitizer.bypassSecurityTrustHtml(htmlFileContent);
this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl("data:text/html;charset=utf-8,"+ this.filedata) ;
这确实会工作 部分:我在输出中收到警告 SafeValue must use [property]=binding:
并且“内部”HTML 在第一个 src="data:..."
图像出现。
我卡在这里了。当然,我的 iFrame 可以只有一个 src
,我不能将 fileUrl
(然后将缩短为 data:text/html;charset=utf-8,
内容)和 fileData
连接为一个是一个 SafeResourceUrl
和一个 SafeHtml
object.
这是我的问题的 MWE:
https://stackblitz.com/edit/angular-ivy-uzpcsy?file=src/app/app.component.ts
(有趣的是,图像是在这里渲染的...无论如何,SafeValue
警告仍然存在。)
对于如何处理这个特殊要求,您有什么建议吗? div
-方法可能仍然是更好的方法吗?非常感谢任何帮助 - 非常感谢!
由于我没有收到也没有找到另一种可能性,所以这是我实现它的方式 - 仅供将来参考:
import { ActivatedRoute, Router, UrlSegment } from '@angular/router';
import { DomSanitizer, SafeHtml, SafeResourceUrl } from '@angular/platform-browser';
export class MyComponent implements OnInit {
filedata: SafeHtml = 'Loading...';
currentUrl: string = '';
constructor(
private activatedRoute: ActivatedRoute,
private sanitizer: DomSanitizer,
) {}
ngOnInit(): void {
// Use the Angular possibilities to retrieve to complete current URL
this.activatedRoute.url.subscribe(url => {
this.currentUrl = '';
url.forEach(urlElement => {
this.currentUrl += urlElement + '/';
});
this.currentUrl = this.currentUrl.substring(0, this.currentUrl.length - 1);
});
this.loadFileData();
}
loadFileData(): void {
// This is the function where the ZIP file is downloaded and the file
// content is extracted. As it is not part of the question or the
// answer, I will not post it here.
// Let's just assume, the file content is now stored in "response".
this.filedata =
this.sanitizer.bypassSecurityTrustHtml(this.correctFileData(response));
}
// Receives the file content response as first parameter,
// removes the unwanted tags and corrects the anchor links.
private correctFileData(response: string): string {
let el = document.createElement('html');
el.innerHTML = response;
// Remove all "head" tags including their inner elements
// (Of course, the should be only one head tag, but you never know...)
let headElements = el.getElementsByTagName('head');
for (let i = 0; i < headElements.length; i++) {
el.removeChild(headElements[i]);
}
// Correct all anchor links: Prepend the current URL
// So http://my.address.com/#anchor would become
// http://my.address.com/myApp/myRoute/mySubRoute#anchor
// for example
let links = el.getElementsByTagName('a');
for (let i = 0; i < links.length; i++) {
let link = links[i];
if (link.href.indexOf('#') != -1) {
console.log(link.href + ' --> ' + this.currentUrl +
link.href.substring(link.href.indexOf('#')));
link.href = this.currentUrl + link.href.substring(link.href.indexOf('#'));
}
}
return el.outerHTML;
}
而在组件的 HTML 中,我只使用:
<div [innerHTML]="filedata"></div>
可能还有其他更好的解决方案,但这个解决方案非常适合我的用例。
在我的Angular申请中,我有以下不变的基本条件:
- 应用程序从外部动态加载 ZIP 文件 URL。
- 在此 ZIP 文件中,有一个要提取的 HTML 文件。此 HTML 文件可能包含具有
data:image/jpeg;base64,...
类来源的图像(但没有外部链接)。 - HTML 文件必须由 Angular 应用程序在单独的浏览器选项卡中“按原样”显示
我设法使用 JSZip 等无缝地实现了 1. 和 2. 因此,我们可以假设,现在有一个 String
变量 htmlFileContent
。在特定条件下在空的 Angular 选项卡中显示 iFrame 也不是问题,我设法做到了这一点。为了实现第 3 点的其余部分,我看到了两种不同的方法:
使用 div:
使用 htmlFileContent
作为 div
元素的 innerHTML
,像这样:
<div [innerHtml]="{{ htmlFileContent }}"></div>
这确实有效,但有一些副作用,例如。 G。 “内部”HTML header 的 title
也在浏览器中呈现。因此,我可以尝试将 htmlFileContent
解析为 DOM 并删除不需要的树元素。这可能是一个可行的解决方案,但不知何故我对此感觉不太好。
此外,HTML 包含一些 in-file 锚链接(<a href="#topOfPage">
样式),这些链接也不再有效,需要更正。
使用 iFrame:
我很清楚 iFrame 使用的丑陋和弃用,但是,在我看来,这似乎是解决我的问题的适当方法。所以我会使用:
<iframe [src]="fileUrl" class="fullscreenIFrame"></iframe>
这里出现了问题:可以设置 fileUrl="data:text/html;charset=utf-8,"+ htmlFileContent
。但是,Angular 会(正确地)抱怨:ERROR Error: NG0904: unsafe value used in a resource URL context (see https://g.co/ng/security#xss)
并且不会显示任何内容。所以,目前我正在尝试使用来自 @angular/platform-browser
:
DomSanitizer
this.filedata = this.sanitizer.bypassSecurityTrustHtml(htmlFileContent);
this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl("data:text/html;charset=utf-8,"+ this.filedata) ;
这确实会工作 部分:我在输出中收到警告 SafeValue must use [property]=binding:
并且“内部”HTML 在第一个 src="data:..."
图像出现。
我卡在这里了。当然,我的 iFrame 可以只有一个 src
,我不能将 fileUrl
(然后将缩短为 data:text/html;charset=utf-8,
内容)和 fileData
连接为一个是一个 SafeResourceUrl
和一个 SafeHtml
object.
这是我的问题的 MWE:
https://stackblitz.com/edit/angular-ivy-uzpcsy?file=src/app/app.component.ts
(有趣的是,图像是在这里渲染的...无论如何,SafeValue
警告仍然存在。)
对于如何处理这个特殊要求,您有什么建议吗? div
-方法可能仍然是更好的方法吗?非常感谢任何帮助 - 非常感谢!
由于我没有收到也没有找到另一种可能性,所以这是我实现它的方式 - 仅供将来参考:
import { ActivatedRoute, Router, UrlSegment } from '@angular/router';
import { DomSanitizer, SafeHtml, SafeResourceUrl } from '@angular/platform-browser';
export class MyComponent implements OnInit {
filedata: SafeHtml = 'Loading...';
currentUrl: string = '';
constructor(
private activatedRoute: ActivatedRoute,
private sanitizer: DomSanitizer,
) {}
ngOnInit(): void {
// Use the Angular possibilities to retrieve to complete current URL
this.activatedRoute.url.subscribe(url => {
this.currentUrl = '';
url.forEach(urlElement => {
this.currentUrl += urlElement + '/';
});
this.currentUrl = this.currentUrl.substring(0, this.currentUrl.length - 1);
});
this.loadFileData();
}
loadFileData(): void {
// This is the function where the ZIP file is downloaded and the file
// content is extracted. As it is not part of the question or the
// answer, I will not post it here.
// Let's just assume, the file content is now stored in "response".
this.filedata =
this.sanitizer.bypassSecurityTrustHtml(this.correctFileData(response));
}
// Receives the file content response as first parameter,
// removes the unwanted tags and corrects the anchor links.
private correctFileData(response: string): string {
let el = document.createElement('html');
el.innerHTML = response;
// Remove all "head" tags including their inner elements
// (Of course, the should be only one head tag, but you never know...)
let headElements = el.getElementsByTagName('head');
for (let i = 0; i < headElements.length; i++) {
el.removeChild(headElements[i]);
}
// Correct all anchor links: Prepend the current URL
// So http://my.address.com/#anchor would become
// http://my.address.com/myApp/myRoute/mySubRoute#anchor
// for example
let links = el.getElementsByTagName('a');
for (let i = 0; i < links.length; i++) {
let link = links[i];
if (link.href.indexOf('#') != -1) {
console.log(link.href + ' --> ' + this.currentUrl +
link.href.substring(link.href.indexOf('#')));
link.href = this.currentUrl + link.href.substring(link.href.indexOf('#'));
}
}
return el.outerHTML;
}
而在组件的 HTML 中,我只使用:
<div [innerHTML]="filedata"></div>
可能还有其他更好的解决方案,但这个解决方案非常适合我的用例。