Electron 中 UploadData 对象详解:请求上传体的结构定义、API 使用与源码实现
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
Electron 的UploadData对象是网络栈向 JS 层暴露"正在上传的请求体"的标准数据结构,主要出现在webRequest事件细节与协议处理相关的结构中。读懂它的三个字段、弄清楚它在session.webRequest监听器中的出现位置,并对照 源码转换器 了解 Chromium 底层四种上传元素是如何映射成 JS 对象的,你就能在拦截请求时准确识别普通字节、磁盘文件、Blob 和流式上传这四类载荷。
UploadData 对象:官方定义与字段语义
UploadData 官方结构定义 非常紧凑,全文只有三个字段,这里完整继承并展开说明:
| 字段 | 类型 | 是否可选 | 说明 |
|---|---|---|---|
bytes | Buffer | 必填 | 正在发送的内容(Content being sent) |
file | string | 可选 | 正在上传的文件路径(Path of file being uploaded) |
blobUUID | string | 可选 | Blob 数据的 UUID,配合 ses.getBlobData 方法取回实际数据 |
三个字段分别对应三种典型的上传场景:
bytes:请求体在内存中、可以直接以Buffer形式读取时(例如fetch上传字符串/ArrayBuffer),转换器会把底层字节复制成 JS Buffer 挂到该字段上;file:上传的是一段磁盘文件(例如multipart/form-data中通过File从fs路径构造的部分)时,JS 层拿到的只是文件路径字符串,文件内容并不会被整体拷贝进内存;blobUUID:上传数据来自 Blob(DataPipe 形式)时,对象上只携带一个 UUID 标识符,需要通过session实例的ses.getBlobData(identifier)方法按标识取回数据——这一点在官方文档中作为blobUUID字段的说明被明确写出。
UploadData 出现的位置
webRequest 事件中的details.uploadData
WebRequest 模块文档 中,uploadData以UploadData[]数组形式出现在两个监听器的details对象里:
webRequest.onBeforeRequest([filter, ]listener):details.uploadData为[UploadData[]](docs/api/structures/upload-data.md)(非可选),文档原文明确写道 "The uploadDatais an array ofUploadDataobjects"。该事件在请求即将发出时触发,监听器必须通过callback返回一个含cancel、redirectURL等可选字段的 response 对象。webRequest.onBeforeSendHeaders([filter, ]listener):details.uploadData为[UploadData[]](docs/api/structures/upload-data.md)(可选,因为并非所有请求都带上传体),此时details中还会附带requestHeaders,适合在发头阶段检查上传内容并改写请求头。
两个监听器都支持通过WebRequestFilter按 URL 过滤,文档给出的合法 URL 模式 包括:
'<all_urls>' 'http://foo:1234/' 'http://foo.com/' '*://*/*' '*://example.com/*' 'http://*.foo:1234/' 'file://foo:1234/bar'ProtocolRequest 结构中的可选uploadData
ProtocolRequest 结构 定义了描述一次请求的对象:url、referrer、method、headers,以及可选的uploadData字段,其类型同样是UploadData[]。也就是说,在协议处理的请求描述中,上传体同样以 UploadData 数组的形式暴露给嵌入方。
源码实现:Chromium 上传数据如何变成 UploadData
从源码结构看,UploadData并不是 Electron 自己构造的数据,而是 Chromium 网络栈请求体对象的 JS 投影。关键转换逻辑位于 Converternetwork::ResourceRequestBody::ToV8:它遍历底层network::ResourceRequestBody的elements(),按DataElement::Tag枚举逐一分发,构造出 JS 对象数组。四种底层元素到 JS 字段的映射关系如下(见 net_converter.cc):
| 底层 mojom 元素 | type字段 | 写入的 JS 属性 |
|---|---|---|
kFile(DataElementFile) | file | file、filePath、offset、length、modificationTime |
kBytes(DataElementBytes) | rawData | bytes(通过electron::Buffer::Copy拷贝字节) |
kDataPipe(Blob) | blob | blobUUID、dataPipe |
kChunkedDataPipe(流) | stream | body(ReadableStream包装) |
可以推断出几个文档未展开、但源码中明确存在的事实:
- 文档字段是"简化视图"。官方结构文档只列了
bytes/file/blobUUID三个字段,而转换器实际还会写入type以及文件元素的filePath、offset、length、modificationTime。type字段(取值file/rawData/blob/stream)可以看作区分四种载荷的判断依据。 - 字节拷贝发生在转换时。
kBytes分支调用electron::Buffer::Copy,意味着bytesBuffer 是底层数据的一份副本;而kFile分支只传递路径,不触发内容读取,对大文件上传的拦截检查(只读路径、校验文件名等)是零拷贝的。 - Blob 的数据管道生命周期绑定在 UploadData 对象上。源码注释写道 "The lifetime of data pipe is bound to the uploadData object",即
dataPipe属性(DataPipeHolder)与uploadData对象同生命周期——如果你要异步处理 Blob 数据,需要保证持有该uploadData对象或及时用getBlobData取回内容。 getBlobData有被重构的意图。源码中的 TODO 注释 表明:在 NetworkService 重构之后,旧的blobUUIDAPI 变得"不必要地复杂",未来计划弃用getBlobData并直接返回DataPipeHolder包装器。因此在使用blobUUID时宜将其视为稳定的当前 API,但不必为它设计过度复杂的持久化方案。
通过 getBlobData 取回 Blob 内容
对携带blobUUID的 UploadData,官方文档给出的取数方式是ses.getBlobData(identifier),其定义位于 Session API 文档。典型的使用链路是:webRequest监听器拿到details.uploadData中某个元素的blobUUID→ 调用对应session的getBlobData→ 按 UUID 换回 Blob 数据。对于file元素,则直接根据file路径用fsAPI 处理即可。
与相近结构的区分
仓库中还有几个容易与UploadData混淆的结构,注意它们的适用场景不同:
- UploadRawData / UploadFile:
{type: 'rawData', bytes}与{type: 'file', filePath, offset, length, modificationTime}两种结构,是 PostBody 对象 中data数组的元素类型。PostBody(含contentType、boundary,contentType只允许application/x-www-form-urlencoded或multipart/form-data,对应 HTML 表单的enctype)用于表单提交场景,与UploadData的网络栈来源是两条不同的暴露路径; - ProtocolResponseUploadData:只有
contentType(MIME 类型)与data(string | Buffer)两个字段,出现在 ProtocolResponse 中,属于协议处理器侧的上传响应数据,与请求拦截侧的UploadData不是一回事。
实战示例:在 webRequest 中检查上传内容
结合上述结构定义,一个合法的拦截检查逻辑如下(字段取值与 UploadData 定义、webRequest 事件细节 完全对应):
const { session } = require('electron') const ses = session.defaultSession ses.webRequest.onBeforeRequest( { urls: ['*://*/*'] }, (details, callback) => { for (const chunk of details.uploadData) { if (chunk.file) { // 磁盘文件上传:只拿到路径,按需再读文件 console.log('file upload:', chunk.file) } else if (chunk.blobUUID) { // Blob 上传:用 UUID 异步取回数据 ses.getBlobData(chunk.blobUUID).then(data => { console.log('blob data size:', data.length) }) } else if (chunk.bytes) { // 内存中的原始字节 console.log('raw bytes:', chunk.bytes.length) } } callback({}) } )注意:onBeforeRequest中uploadData是必填字段,而onBeforeSendHeaders中它是可选的,后者使用时应先做空值判断。
小结
UploadData由bytes(Buffer,必填)、file(string,可选)、blobUUID(string,可选)三字段组成,分别覆盖内存字节、磁盘文件、Blob 三类上传内容;- 它主要出现在
webRequest的onBeforeRequest/onBeforeSendHeaders细节与ProtocolRequest结构中,以数组形式暴露; - 从源码看,它由 net_converter.cc 将 Chromium 的
ResourceRequestBody四种DataElement(file / rawData / blob / stream)转换而来,实际对象上还带有type等文档未列出的字段; - 取回 Blob 数据使用
ses.getBlobData(identifier);与PostBody、ProtocolResponseUploadData等相似结构应明确区分场景后再使用。
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考