You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中用RxJS Subject服务传递Blob跨组件失败求助

问题根源

你的代码无法传递Blob的核心原因有两个:

  1. 跨标签页服务不共享:新打开的标签页是一个独立的Angular应用实例,原页面的DocPreviewService和新标签页的服务实例完全隔离,RxJS Subject的消息无法跨实例传递。
  2. 时机不匹配:预览组件的ngAfterViewInit执行时,异步请求大概率还未完成,此时doc_blob仍为undefined,不会触发渲染逻辑。

解决方案一:通过Object URL传递(推荐)

利用浏览器的URL.createObjectURL生成Blob的临时访问地址,将其作为路由参数传递给新标签页,预览组件直接通过该地址获取Blob并渲染。

1. 修改第一个组件的showReport方法

showReport(selected_id: string) {
  this.report_service.createReport(this.calc_items[selected_id]).subscribe({
    next: (doc_blob: Blob) => {
      // 生成Blob的临时URL
      const blobUrl = URL.createObjectURL(doc_blob);
      // 将URL作为查询参数传入路由
      const url = this.router.serializeUrl(
        this.router.createUrlTree([`tools/${selected_id}/preview`], {
          queryParams: { blobUrl }
        })
      );
      window.open(url, '_blank');
    },
    error: (error: any) => {
      console.error('获取报告失败:', error);
    },
  });
}

2. 修改预览组件

import { ActivatedRoute } from '@angular/router';

export class DocPreviewComponent implements OnInit, AfterViewInit, OnDestroy {
  doc_blob?: Blob;
  @ViewChild('doc_preview') doc_preview!: ElementRef;
  private blobUrl?: string;
  
  constructor(
    private route: ActivatedRoute,
    private local_storage: LocalStorageService
  ) {}

  ngOnInit(): void {
    // 从路由参数获取Blob的临时URL
    this.route.queryParams.subscribe(params => {
      this.blobUrl = params['blobUrl'];
      if (this.blobUrl) {
        // 通过fetch获取Blob
        fetch(this.blobUrl)
          .then(res => res.blob())
          .then(blob => {
            this.doc_blob = blob;
            this.renderDoc();
          });
      }
    });
  }

  ngAfterViewInit(): void {
    // 若初始化时已获取到Blob,直接渲染
    if (this.doc_blob) {
      this.renderDoc();
    }
  }

  private renderDoc(): void {
    if (this.doc_blob && this.doc_preview) {
      doc.renderAsync(this.doc_blob, this.doc_preview.nativeElement)
        .then(() => {
          console.log("docx: finished");
          // 渲染完成后释放临时URL,避免内存泄漏
          if (this.blobUrl) {
            URL.revokeObjectURL(this.blobUrl);
          }
        });
    }
  }

  ngOnDestroy(): void {
    // 组件销毁时释放临时URL
    if (this.blobUrl) {
      URL.revokeObjectURL(this.blobUrl);
    }
  }
}

解决方案二:通过LocalStorage传递(适合大Blob场景)

将Blob转成Base64字符串存入LocalStorage,新标签页读取后转回Blob,渲染完成后清除存储内容。

1. 修改第一个组件的showReport方法

showReport(selected_id: string) {
  this.report_service.createReport(this.calc_items[selected_id]).subscribe({
    next: (doc_blob: Blob) => {
      const reader = new FileReader();
      reader.onload = () => {
        const base64Data = reader.result as string;
        // 用selected_id作为存储键,避免冲突
        localStorage.setItem(`preview_blob_${selected_id}`, base64Data);
        const url = this.router.serializeUrl(
          this.router.createUrlTree([`tools/${selected_id}/preview`])
        );
        window.open(url, '_blank');
        // 1分钟后自动清除存储,防止冗余数据
        setTimeout(() => {
          localStorage.removeItem(`preview_blob_${selected_id}`);
        }, 60000);
      };
      reader.readAsDataURL(doc_blob);
    },
    error: (error: any) => {
      console.error('获取报告失败:', error);
    },
  });
}

2. 修改预览组件

import { ActivatedRoute } from '@angular/router';

export class DocPreviewComponent implements OnInit, AfterViewInit, OnDestroy {
  doc_blob?: Blob;
  @ViewChild('doc_preview') doc_preview!: ElementRef;
  private selectedId?: string;
  
  constructor(
    private route: ActivatedRoute,
    private local_storage: LocalStorageService
  ) {}

  ngOnInit(): void {
    this.route.paramMap.subscribe(params => {
      this.selectedId = params.get('selected_id');
      if (this.selectedId) {
        const base64Data = localStorage.getItem(`preview_blob_${this.selectedId}`);
        if (base64Data) {
          // 将Base64转成Blob
          const byteString = atob(base64Data.split(',')[1]);
          const mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
          const ab = new ArrayBuffer(byteString.length);
          const ia = new Uint8Array(ab);
          for (let i = 0; i < byteString.length; i++) {
            ia[i] = byteString.charCodeAt(i);
          }
          this.doc_blob = new Blob([ab], { type: mimeString });
          this.renderDoc();
          // 读取后立即清除存储
          localStorage.removeItem(`preview_blob_${this.selectedId}`);
        }
      }
    });
  }

  ngAfterViewInit(): void {
    if (this.doc_blob) {
      this.renderDoc();
    }
  }

  private renderDoc(): void {
    if (this.doc_blob && this.doc_preview) {
      doc.renderAsync(this.doc_blob, this.doc_preview.nativeElement)
        .then(() => console.log("docx: finished"));
    }
  }

  ngOnDestroy(): void {}
}

内容的提问来源于stack exchange,提问作者sakayevas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:52:43