Angular多选文件预览异常问题排查求助
问题排查与解决方案
核心原因
循环中使用var声明FileReader实例,var属于函数级作用域,所有循环迭代共享同一个reader变量。当异步的onload回调触发时,循环已经执行完毕,reader指向最后一个文件的FileReader实例:
- 前几个文件的
onload回调触发时,对应的reader已经被后续循环覆盖,此时最后一个文件的读取可能还未完成,导致reader.result为null - 最后一个文件的
onload触发时,它的reader刚好完成读取,所以能拿到正确的结果
修复代码
修改component.ts中的onFilesSelected方法
将var reader改为let reader,让每个循环迭代拥有独立的FileReader实例;同时可以改用event.target.result获取结果,进一步避免作用域问题:
onFilesSelected(event: any): void { this.selectedFiles = event.target.files; for (let i = 0; i < this.selectedFiles.length; i++) { const fileWithImage: FileWithImage = { imageUrl: '', file: this.selectedFiles[i], }; // 用let声明,每个循环迭代独立作用域 let reader = new FileReader(); reader.readAsDataURL(fileWithImage.file); reader.onload = (event) => { // 用event.target.result替代reader.result,更安全 fileWithImage.imageUrl = event.target?.result as string; this.filesWithImages.push(fileWithImage); }; } }
可选优化:改用for...of循环(更简洁)
onFilesSelected(event: any): void { this.selectedFiles = event.target.files; for (const file of this.selectedFiles) { const fileWithImage: FileWithImage = { imageUrl: '', file, }; let reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (e) => { fileWithImage.imageUrl = e.target?.result as string; this.filesWithImages.push(fileWithImage); }; } }
添加删除已选文件功能
在component.ts中添加删除方法
removeFile(index: number): void { this.filesWithImages.splice(index, 1); }
修改component.html,添加删除按钮
<button type="button" mat-raised-button (click)="fileInput.click()" class="button" > Choose File </button> <input hidden (change)="onFilesSelected($event)" #fileInput type="file" accept="image/*" multiple="multiple" /> <div *ngFor="let file of filesWithImages; let i = index" class="images"> <img [src]="file.imageUrl" height="10%" width="10%" *ngIf="file.imageUrl" /> <!-- 添加删除按钮 --> <button (click)="removeFile(i)" class="delete-btn">删除</button> </div>
为什么之前的尝试无效
- 用标记位阻塞循环:FileReader的读取是异步非阻塞的,阻塞循环会导致UI线程卡死,且无法解决
var的作用域问题 - 在img的[src]中直接调用方法:方法如果返回异步结果会导致频繁触发,甚至陷入无限变更检测循环,最终导致程序崩溃
内容的提问来源于stack exchange,提问作者cinawiki
相关产品推荐
相关产品推荐

