☰
ng-zorro-antd Upload 上传前转换文件:借助 nzBeforeUpload 在请求发出前为文件添加水印
2026/10/6 2:36:40 网站建设 项目流程
  • UI组件
  • 前端

【免费下载链接】ng-zorro-antd

Angular UI Component Library based on Ant Design

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载

在 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(); }); }; }; }); }

要点提炼:

  1. 模板侧:nz-upload上通过[nzBeforeUpload]="transformFile"绑定处理函数;nzAction指定上传地址(示例为演示用的 mock 接口)。
  2. 处理函数签名:接收NzUploadFile,返回Observable<Blob>——即把"转换后的文件"以异步流的形式交还上传组件。
  3. 必须使用箭头函数定义:官方 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

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载
上一篇:网易云音乐Web端播放器 NetEaseMusic:3 步在本地播放 VIP 与下架歌曲
下一篇:游戏 DLSS 版本替换不用重装:DLSS Swapper 快速上手完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询