Angular如何在新标签页打开已上传未入库的本地文件
Angular文件上传后点击新标签页打开实现方案
问题背景
当前已在Angular应用中实现基础文件上传功能,支持上传除.exe、.sql等少数禁用类型外的任意格式文件:
- 初始前端模板代码:
<input type="file" id="file" #file (change)="fileUploadHandler($event)" multiple>
- 初始文件上传处理逻辑(
fileupload.component.ts):
fileUploadHandler(event: Event) { const inputElement = event.target as HTMLInputElement; if (inputElement.files && inputElement.files?.length) { _each(inputElement.files, (_file) => { const fileSize = _file.size; if (fileSize < this.MAX_DOC_SIZE) { const fileName = _file.name?.trim(); const doc = new Document(); doc.name = fileName; doc.extension = fileName.slice(fileName.lastIndexOf('.') + 1); doc.size = fileSize; this.docs.push(doc); } }); }}
现有逻辑会将符合大小要求的文件信息解析后存入docs数组,在UI层渲染为文件列表,展示效果如下:
待解决问题
需要实现交互效果:用户点击列表中的某一文件时,该文件直接在新标签页打开。
之前尝试使用FileReader编写的代码未生效,控制台无有效数据输出,问题代码如下:
_each(inputElement.files, (_file) => { const reader = new FileReader(); const data = reader.readAsDataURL(_file); console.log(data) // 控制台无内容输出 })
问题原因
- 原有
doc数据结构仅存储了文件名、后缀、大小,未存储原始文件引用或可访问的文件地址,点击事件触发时无法获取文件源 FileReader.readAsDataURL是异步API,调用后不会同步返回结果,直接打印返回值拿不到读取到的内容,必须等待加载完成事件触发后才能从实例上获取结果- 对于文件预览打开场景,
FileReader读取全量文件转base64的方案性能差,大文件下内存占用高,不是最优实现
实现步骤
1. 改造上传处理逻辑,生成文件临时访问地址
优先使用URL.createObjectURL生成Blob临时地址,相比FileReader方案性能更高、内存占用更小。改造后上传逻辑如下:
fileUploadHandler(event: Event) { const inputElement = event.target as HTMLInputElement; if (inputElement.files && inputElement.files?.length) { _each(inputElement.files, (_file) => { const fileSize = _file.size; if (fileSize < this.MAX_DOC_SIZE) { const fileName = _file.name?.trim(); const doc = new Document(); doc.name = fileName; doc.extension = fileName.slice(fileName.lastIndexOf('.') + 1); doc.size = fileSize; // 存储原始文件引用 doc.rawFile = _file; // 生成同源临时访问地址 doc.previewUrl = URL.createObjectURL(_file); this.docs.push(doc); } }); }}
注意:需要先给
Document类新增rawFile: File、previewUrl: string两个类型定义,避免TS类型报错。
2. 模板绑定列表项点击事件
在文件列表渲染位置,给每个文件项绑定点击触发方法,示例:
<div class="file-list"> <div *ngFor="let doc of docs" class="file-item" (click)="openFileInNewTab(doc)"> <span class="file-name">{{doc.name}}</span> <span class="file-size">{{doc.size | filesize}}</span> </div> </div>
3. 实现新标签页打开方法
在组件类中新增打开方法,直接调用window.open打开之前生成的临时地址即可,浏览器会自动识别可预览格式(图片、PDF、纯文本等)在新标签页直接展示:
openFileInNewTab(doc: Document) { window.open(doc.previewUrl, '_blank'); }
4. 处理内存泄漏
在组件销毁生命周期中,释放所有生成的Blob临时地址,避免内存占用:
import { Component, OnDestroy } from '@angular/core'; export class FileuploadComponent implements OnDestroy { // ... 其他原有代码 ngOnDestroy(): void { this.docs.forEach(doc => { if (doc.previewUrl) { URL.revokeObjectURL(doc.previewUrl); } }) } }
附:FileReader正确写法(不推荐,仅作参考)
如果一定要用FileReader实现,必须监听onload事件等待读取完成再拿结果:
_each(inputElement.files, (_file) => { const reader = new FileReader(); reader.onload = () => { // 读取完成后才能拿到base64结果 const data = reader.result; console.log(data); // 此处将data赋值给doc的预览地址字段即可 } reader.readAsDataURL(_file); })
内容的提问来源于stack exchange,提问作者Kgn-web
相关产品推荐
相关产品推荐

