- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
在 ng-zorro-antd 的nz-upload组件中,nzBeforeUpload是连接"用户选中文件"与"文件真正上传到服务器"之间的关键钩子,它不只用于校验拦截,还能在请求发出之前改写文件内容本身。本指南以官方演示transform-file为骨架,完整讲解如何用nzBeforeUpload结合 FileReader、Canvas 与 RxJS Observable 在浏览器端为图片添加水印,并深入源码剖析其底层调用链、返回值类型与 uid 复用机制,帮助你在此基础上实现图片压缩、格式校验、异步审核等各类"上传前处理"需求。
一、问题背景:为什么需要"上传前转换"?
在很多业务场景中,用户选中的文件并不能直接上传到服务器:
- 图片需要加水印(版权、来源标识);
- 原图过大需要压缩、缩放后再上传以节省带宽与存储;
- 上传前需要异步校验(如请求服务端检查文件合法性);
- 需要把文件改写为另一种格式(如截图转 Blob)。
nzBeforeUpload正好为这些需求提供了一个统一入口。官方演示transform-file(见 components/upload/demo/transform-file.ts)的说明非常凝练:"使用nzBeforeUpload在请求之前转换文件,例如添加水印"。它展示了一条完整的"读文件 → 画布绘制水印 → 转 Blob → 交还上传组件"的流水线。
二、先看完整示例:Canvas 加水印的官方实现
演示组件的模板与处理函数完整代码如下(取自 components/upload/demo/transform-file.ts):
import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzUploadFile, NzUploadModule } from 'ng-zorro-antd/upload'; @Component({ selector: 'nz-demo-upload-transform-file', imports: [NzButtonModule, NzIconModule, NzUploadModule], template: ` <nz-upload nzAction="https://www.mocky.io/v2/5cc8019d300000980a055e76" [nzBeforeUpload]="transformFile"> <button nz-button> <nz-icon nzType="upload" /> Upload </button> </nz-upload> ` }) export class NzDemoUploadTransformFileComponent { transformFile = (file: NzUploadFile): Observable<Blob> => new Observable(observer => { const reader = new FileReader(); reader.readAsDataURL(file as NzSafeAny); reader.onload = () => { const canvas = document.createElement('canvas'); const img = document.createElement('img'); img.src = reader.result as string; img.onload = () => { const ctx = canvas.getContext('2d')!; ctx.drawImage(img, 0, 0); ctx.fillStyle = 'red'; ctx.textBaseline = 'middle'; ctx.fillText('Ant Design', 20, 20); canvas.toBlob(blob => { observer.next(blob!); observer.complete(); }); }; }; }); }要点提炼:
- 模板侧:
nz-upload上通过[nzBeforeUpload]="transformFile"绑定处理函数;nzAction指定上传地址(示例为演示用的 mock 接口)。 - 处理函数签名:接收
NzUploadFile,返回Observable<Blob>——即把"转换后的文件"以异步流的形式交还上传组件。 - 必须使用箭头函数定义:官方 API 文档(components/upload/doc/index.zh-CN.md)明确要求
nzBeforeUpload务必使用=>定义处理方法,以保证this指向组件实例。
三、逐段拆解:本地图片转换的完整流程
transformFile内部是一条标准的浏览器端图片处理管线,共五步:
1. 用 FileReader 把文件读为 Data URL
const reader = new FileReader(); reader.readAsDataURL(file as NzSafeAny);FileReader.readAsDataURL将选中的图片文件异步读取为 base64 形式的 Data URL。此时file虽是NzUploadFile类型,但底层就是浏览器File对象,因此可直接传入。由于读取是异步的,后续逻辑都挂在reader.onload回调里。
2. 创建画布与图片对象
const canvas = document.createElement('canvas'); const img = document.createElement('img'); img.src = reader.result as string;用读取结果作为img的src,并创建一块画布。canvas的尺寸默认与绘制内容一致,这里未显式设置宽高,drawImage会以图片原始尺寸绘制。
3. 在画布上绘制原图与水印
img.onload = () => { const ctx = canvas.getContext('2d')!; ctx.drawImage(img, 0, 0); ctx.fillStyle = 'red'; ctx.textBaseline = 'middle'; ctx.fillText('Ant Design', 20, 20); ... };img.onload确保图片解码完成后才开始绘制。drawImage(img, 0, 0)先把原图完整画上,随后用fillStyle设置水印颜色(示例为红色)、textBaseline = 'middle'设置文本基线,再通过fillText在坐标 (20, 20) 处写入 "Ant Design" 文本水印。
4. 导出为 Blob
canvas.toBlob(blob => { observer.next(blob!); observer.complete(); });canvas.toBlob把画布内容异步导出为Blob对象(默认 PNG 格式)。得到结果后,通过observer.next(blob)把转换后的文件发射出去,并observer.complete()结束 Observable 流。
5. 用 Observable 包装整个异步过程
外层new Observable(observer => { ... })把"读取文件 → 解码图片 → 绘制水印 → 导出 Blob"这一串浏览器异步回调,统一收敛为单一的可订阅流,正好与nzBeforeUpload支持的Observable返回值对接。
四、源码级原理:nzBeforeUpload 在内部如何被消费
了解调用链能让你清楚"返回 Blob"与"返回 true"究竟有什么区别。
1. 输入定义
在 components/upload/upload.component.ts 中,钩子被声明为:
@Input() nzBeforeUpload?: (file: NzUploadFile, fileList: NzUploadFile[]) => NzBeforeUploadFileType;而返回类型定义在 components/upload/interface.ts:
export type NzBeforeUploadFileType = | boolean | Observable<boolean | NzUploadFile | Blob | File | boolean> | Promise<boolean | NzUploadFile | Blob | File | boolean>;即返回值可以是同步布尔值,也可以是Observable或Promise;异步流中的值可以是boolean、NzUploadFile、Blob或File。
2. 消费逻辑:upload-btn.component.ts
nzUpload组件会把nzBeforeUpload打包进ZipButtonOptions(见 components/upload/upload.component.ts),最终由按钮组件 components/upload/upload-btn.component.ts 的upload()方法消费:
private upload(file: NzUploadFile, fileList: NzUploadFile[]): void { if (!this.options.beforeUpload) { return this.post(file); } const before = this.options.beforeUpload(file, fileList); const successBeforeLoadHook = (processedFile: NzUploadFile | boolean | Blob | File): void => { const processedFileType = Object.prototype.toString.call(processedFile); if ( typeof processedFile !== 'boolean' && (processedFileType === '[object File]' || processedFileType === '[object Blob]') ) { (processedFile as NzUploadFile).uid = file.uid; // 转换后的文件必须沿用原文件的 uid this.post(file, processedFile as NzUploadFile); } else if (processedFile) { this.post(file); } }; ... if (before instanceof Observable) { before.subscribe({ next: successBeforeLoadHook, error: errorBeforeLoadHook }); } else if (before instanceof Promise) { before.then(successBeforeLoadHook).catch(errorBeforeLoadHook); } else if (before) { return this.post(file); } }这段代码揭示了三个关键机制:
- 分支分发:返回值是
Observable就订阅、是Promise就.then、是同步真值就直接继续上传。转换后的结果统一进入successBeforeLoadHook。 - Blob/File 判定:当回调结果不是布尔值、且原型是
[object File]或[object Blob]时,说明文件被替换了,此时以this.post(file, processedFile)上传新文件;否则视为"放行",上传原始文件。 - uid 继承:源码注释明确指出转换后的文件必须沿用原文件的
uid,以保证文件列表的追踪、进度上报和删除逻辑一致。
3. 真正发出请求时用哪个文件
在post()方法中(components/upload/upload-btn.component.ts),processedFile会被放入NzUploadXHRArgs.postFile,最终在默认的xhr()实现里通过formData.append(args.name, args.postFile)随请求发送(见 components/upload/upload-btn.component.ts)。也就是说,服务器收到的是转换后的 Blob,而不是用户原始文件——这正是"上传前转换"的最终效果。
五、返回值语义速查表
结合 components/upload/doc/index.zh-CN.md 的 API 说明与上述源码,nzBeforeUpload的返回值语义可归纳如下:
| 返回值 | 行为 | 典型用途 |
|---|---|---|
false(同步) | 停止上传 | 格式、大小校验不通过时拦截 |
true(同步) | 直接上传原始文件 | 校验通过放行 |
Promise<boolean> | 异步决定放行或拦截 | 异步校验(如请求服务端) |
Promise<File/Blob> | 异步替换后上传新文件 | 异步转换(如调用第三方 API 处理图片) |
Observable<boolean> | 订阅后按布尔值放行/拦截 | 异步校验,流式取消更灵活 |
Observable<File/Blob> | 订阅后上传转换结果 | 本示例的水印场景 |
补充说明两点:
- "放行"与"替换"的区分:只有返回非布尔值的
File/Blob才会替换上传内容;返回true只是放行,仍上传原文件。 - 拦截语义:官方另一个演示
png-only(见 components/upload/demo/png-only.md)明确指出,nzBeforeUpload仅在返回false、Promise.reject()或 Observable 抛出错误时才阻止上传;回调抛出的错误会以Unhandled upload beforeUpload error的警告输出(见 components/upload/upload-btn.component.ts)。
六、测试印证:Blob 替换上传确实生效
仓库中的测试对"转换文件"行为有直接覆盖。在 components/upload/upload.spec.ts 中:
it('can return a blob file', () => { let ret = false; instance.beforeUpload.set((): Observable<NzSafeAny> => { ret = true; return of(new Blob([JSON.stringify(1, null, 2)], { type: 'application/json' })); }); fixture.detectChanges(); pageObject.postSmall(); expect(ret).toBe(true); });同文件中还覆盖了Observable返回true、返回原文件、返回字符串,以及返回false取消上传(nzChange不被触发)、Promise 返回false取消上传、Promise reject 取消上传等多种分支(见 components/upload/upload.spec.ts),与第四节分析的消费逻辑一一对应。这意味着"返回 Blob 继续上传"是一条被测试保障的稳定能力,你可以放心在生产中使用。
七、实战扩展:从水印到更多"上传前处理"
掌握了nzBeforeUpload的机制后,可以低成本衍生出多种能力:
1. 图片压缩与缩放
复用transformFile的管线,在drawImage前按目标尺寸缩放画布即可:
const scale = 0.5; // 压缩到一半尺寸 canvas.width = img.width * scale; canvas.height = img.height * scale; ctx.drawImage(img, 0, 0, canvas.width, canvas.height);导出的 Blob 体积会显著小于原图,适合移动端上传场景。
2. 头像上传:格式与大小限制
官方avatar演示(见 components/upload/demo/avatar.md)展示了nzBeforeUpload的校验用法:点击上传头像时限制图片格式与大小,且其说明指出返回值可以是一个 Observable 以支持异步检查(配套代码见 components/upload/demo/avatar.ts)。
3. 上传前异步审核
将校验逻辑放入Observable或Promise中,先请求服务端确认文件合法性,再决定返回true放行或返回false拦截,无需改动任何上传代码。
八、注意事项
- IE9 不支持:官方 API 文档明确标注
nzBeforeUpload在 IE9 下不可用(components/upload/doc/index.zh-CN.md)。 - 务必使用箭头函数:
nzBeforeUpload处理函数内部如果依赖组件实例(如调用服务),箭头函数才能保证this正确指向(components/upload/doc/index.zh-CN.md)。 - uid 由组件接管:转换后返回 Blob/File 时无需自己生成 uid,组件内部会自动沿用原文件的 uid(components/upload/upload-btn.component.ts)。
- 浏览器 API 依赖:FileReader、Canvas、
toBlob均为浏览器能力,仅适用于支持这些 API 的现代环境;Canvas 导出 Blob 时若涉及跨域图片,还需注意 CORS 与画布污染问题。
小结
nzBeforeUpload是 ng-zorro-antd Upload 组件"上传前处理"的唯一官方入口:它既能同步返回布尔值拦截文件,也能通过Observable/Promise异步放行,更能在返回Blob/File时整体替换上传内容。官方transform-file演示以"Canvas 加水印"为例,串起了 FileReader、Canvas、toBlob 与 RxJS 的完整链路;理解了 components/upload/upload-btn.component.ts 中的消费逻辑后,图片压缩、格式转换、异步审核等更多定制场景都可以用同样的模式实现。
- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
相关推荐
Grafana Pyroscope 1.17 发布说明详解:Exemplar 查询、符号化性能与稳定性增强
Grafana Pyroscope 1.17 发布说明详解:Exemplar 查询、符号化性能与稳定性增强 Grafana Pyroscope 1.17.0 是
UI组件前端如何使用ng-zorro-antd实现大文件上传:高效断点续传完整指南
如何使用ng zorro antd实现大文件上传:高效断点续传完整指南 ng zorro antd是基于Ant Design设计规范的Angular UI组件库
UI组件前端构建安全的PHP命令行应用:ShellWrap最佳实践指南
构建安全的PHP命令行应用:ShellWrap最佳实践指南 在PHP开发中,安全地执行命令行操作一直是个挑战。ShellWrap作为一个优雅的PHP命令行包装库
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考