Skip to content

Commit 2cd547f

Browse files
committed
🎨 Add progress option to customize the upload body size computation
1 parent 6077eb6 commit 2cd547f

File tree

1 file changed

+38
-12
lines changed

1 file changed

+38
-12
lines changed

src/addons/progress.ts

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ConfiguredMiddleware, Wretch, WretchAddon, WretchResponseChain } from "../types.js"
1+
import type { ConfiguredMiddleware, Wretch, WretchAddon, WretchOptions, WretchResponseChain } from "../types.js"
22

33
export type ProgressCallback = (loaded: number, total: number) => void
44

@@ -105,6 +105,22 @@ function toStream<T extends Request | Response>(requestOrResponse: T, bodySize:
105105
}
106106
}
107107

108+
const defaultGetUploadTotal = async (url: string, opts: WretchOptions): Promise<number> => {
109+
let total =
110+
opts.body instanceof ArrayBuffer ? +opts.body.byteLength :
111+
opts.body instanceof Blob ? +opts.body.size :
112+
0
113+
try {
114+
// Try to determine body size by reading it as a blob
115+
total ||= (await new Request(url, opts).blob()).size
116+
} catch {
117+
// Cannot determine body size
118+
}
119+
120+
return total
121+
}
122+
123+
108124
/**
109125
* Adds the ability to monitor progress when downloading a response.
110126
*
@@ -121,7 +137,22 @@ function toStream<T extends Request | Response>(requestOrResponse: T, bodySize:
121137
* .progress((loaded, total) => console.log(`${(loaded / total * 100).toFixed(0)}%`))
122138
* ```
123139
*/
124-
const progress: () => WretchAddon<ProgressAddon, ProgressResolver> = () => {
140+
const progress: (options?: {
141+
/**
142+
* Function used to determine the total upload size before streaming the request body.
143+
* Receives the final request URL and options, returns the total byte size (sync or async). Defaults to trying the `byteLength` property
144+
* for `ArrayBuffer` and the `.size` property for `Blob` (e.g., `FormData` or `File`), then falling back to `Request#blob()` when available.
145+
*
146+
* _Note_: The fallback of using `Request#blob()` is memory consuming as it loads the entire body into memory.
147+
*
148+
* @param url The request URL
149+
* @param opts The request options
150+
* @returns The total upload size in bytes
151+
*/
152+
getUploadTotal?: (url: string, opts: WretchOptions) => number | Promise<number>
153+
}) => WretchAddon<ProgressAddon, ProgressResolver> = ({
154+
getUploadTotal = defaultGetUploadTotal
155+
} = {}) => {
125156
function downloadMiddleware(state: Record<any, any>) : ConfiguredMiddleware {
126157
return next => (url, opts) => {
127158
return next(url, opts).then(response => {
@@ -141,16 +172,11 @@ const progress: () => WretchAddon<ProgressAddon, ProgressResolver> = () => {
141172
return next(url, opts)
142173
}
143174

144-
let bodySize: number = 0
145-
146-
try {
147-
bodySize = (await new Request("a:", { method: "POST", body }).blob()).size
148-
} catch {
149-
// Unable to determine body size
150-
}
151-
152-
const request = toStream(new Request(url, opts), bodySize, state.upload)
153-
return next(request.url, request)
175+
const streameableRequest = toStream(
176+
new Request(url, opts),
177+
await getUploadTotal(url, opts), state.upload
178+
)
179+
return next(url, streameableRequest)
154180
}
155181
}
156182

0 commit comments

Comments
 (0)