Open1
HonoでXHR.upload.onprogress
import type { ClientRequestOptions } from 'hono';
import type { ClientResponse } from 'hono/client';
import type { StatusCode } from 'hono/utils/http-status';
export function $upload<D extends Record<string, unknown>, R>(request: {
$post: (
args: { form: D },
options?: ClientRequestOptions<unknown>,
) => Promise<ClientResponse<R, StatusCode, 'json'>>;
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
$url: (arg?: {} | undefined) => URL;
}) {
const url = request.$url({});
return async (
args: D,
progress?: (uploaded: number, total: number) => Promise<void> | void,
): Promise<R> => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url);
const formData = new FormData();
for (const [key, value] of Object.entries(args)) {
formData.append(key, value as string);
}
if (progress) {
xhr.upload.addEventListener('progress', (e) => {
void progress(e.loaded, e.total);
});
}
return new Promise<R>((resolve) => {
xhr.addEventListener('load', () => {
if (xhr.status < 400) {
resolve(JSON.parse(xhr.responseText));
}
});
xhr.send(formData);
});
};
}
import type { AppType } from './app.js';
import { hc } from 'hono/client';
const client = hc<AppType>(location.origin);
// app.post('/api/upload', zValidator('form', { ... }))と定義されてるとする
const resultJson = await $upload(client.api.upload)({ ... }, (uploaded, total) => {
console.log(`Uploaded: ${uploaded} / ${total} = ${Math.floor((uploaded / total) * 100)}%`);
})