相关文章推荐
有腹肌的卡布奇诺  ·  js ...·  1 周前    · 
非常酷的匕首  ·  WPF ...·  2 年前    · 

Ultimate solution for upload progress monitoring with fetch() / fetch()上传进度监控的终极方案

陶马文 / Marvin Tao

    把你的服务器改成HTTP/2。我的服务器后台主要是Node/Koa,Node提供的 http2 模块可以无缝将你的Koa服务器改为HTTP/2,无需改变其他现有代码。

    Change your server to support HTTP/2. I mainly use Node/Koa as my back-end server. The http2 module shipped along with Node can change your Koa server into HTTP/2 seamlessly, without changing any other code.

    2.

    如果你读过 http2 的文档,或者简单 console.log 输出 ctx.req / ctx.res 对象,你可能已经发现了,他们事实上都是Node中的 Stream 对象,即可以按照流的形式读取。那么你可以在流读取通过 fetch 上传的内容 ( ctx.req ) 的 同时 ,流写入 ctx.res 读取的进度。本质上这就是Server-Sent Event (SSE)的实现。SSE基于HTTP/2长连接,通过流的形式实现服务器向客户端持续的事件推送。SSE的实现基于文本,有一套自己的格式。

    If you have ever read the documentation about http2 , or simply output the ctx.req / ctx.res object with console.log , then you probably had found that both of them are Stream object of Node. You are able to read the content uploaded via fetch in a stream manner, and write to the the ctx.res at the same time. It is essentially an implementation of Server-Sent Event (SSE). SSE is built upon the HTTP/2 long connection, and realize continuous event pushing in stream form. SSE is text, and has its own format.

    3.

    如果你打算用SSE,又想省事,那么就把你的fetch替换为 @microsoft /fetch-event-source。如果你去看源码会发现,fetch-event-source事实上是对fetch的一个封装,主要是对执行fetch后得到的Response对象进行了一些处理,使得它可以兼容SSE,并返回一个EventSource对象。当然你可以定义自己的事件格式而不用SSE,只要定义上传进度的文本格式和读写方法就可以了。

    If you wanted to use SSE, and not willing to do everything from the scratch, then replace your built-in fetch with @microsoft/fetch-event-source. If you have ever read the source code, you may find that fetch-event-source is kind of a wrapper of fetch , which add some extra processing to the Response object returned by fetch, in order to make it compatible with SSE by returning an EventSource object. Of course you can define your own format instead of SSE. You just define your own text format that indicates the progress, and the method of reading and writing.

    4.

    但是需要注意的是,如果你的前端在使用React,还有一个可能会遇到的问题是读取Response流的chunk并不总仅包含一个事件(由服务器返回的上传进度消息)。你需要将流读取到buffer,再进行parse,再一条一条地更新。但是在React中的状态更新是不能在一个循环中进行的。因此你需要将流封装到一个generator function或iterator中,然后在更新组建状态的机制(如 componentDidMount / useEffect )中使用迭代器的 .next() 方法来逐条更新状态。

    It is also noteworthy that you may encounter a problem if you are using React as your front-end framework. You read the Response stream chunk by chunk, while the chunk does not always include exactly one event (the message of upload progress returned from server). You need to read the stream into a buffer, parse it, and then update the corresponding state one message by another.

    However, you cannot set state in a loop, since the state setting is done in batch. So you need to wrap the stream inside a generator function or iterator, and then use the .next() method to update the state in componentDidMount or useEffect .

发布于 2022-03-21 14:59