Submitting assignments or project screenshots involves waiting. Clear progress and retry states keep students calm.
Key terms
- Multipart upload: the HTTP format for sending files; set
enctype="multipart/form-data"on the form. - Progress event: the information emitted from
onprogressthat reports bytes sent so you can compute percentages. - XMLHttpRequest: the older browser API that still offers granular upload control and progress events.
AbortController/xhr.abort(): mechanisms that cancel network requests immediately when the user presses stop.
Core ideas
Multipart transfer splits files into chunks so the server can parse them; browsers only send files when enctype="multipart/form-data" is present. Progress equals the ratio of bytes uploaded to total bytes, available via XMLHttpRequest or fetch with streams. Offer cancellation with xhr.abort() or AbortController so user intent takes effect instantly.
Code examples
Form markup, progress handling, and server actions all matter.
<script>
let pending = false;
let progress = 0;
function handleUpload({ action, form }) {
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (event) => {
if (!event.lengthComputable) return;
progress = Math.round((event.loaded / event.total) * 100);
};
xhr.onloadstart = () => { pending = true; };
xhr.onloadend = () => { pending = false; progress = 0; };
xhr.open('POST', action);
xhr.send(new FormData(form));
return () => xhr.abort();
}
</script>
<form method="POST" enctype="multipart/form-data" use:enhance={handleUpload}>
<input type="file" name="attachments" accept="image/*,application/pdf" multiple required />
<textarea name="comment" placeholder="Notes" rows="3"></textarea>
<button type="submit" disabled={pending}>{pending ? 'Uploading…' : 'Submit'}</button>
</form>
<div class="progress" aria-live="polite">
<div class="bar" style={`width:${progress}%`}>
<span>{progress}%</span>
</div>
</div>
// src/routes/(app)/uploads/+page.server.ts
export const actions = {
default: async ({ request, locals }) => {
const data = await request.formData();
const files = data.getAll('attachments');
if (files.length > 5) {
return fail(400, { error: 'You can upload up to 5 files.' });
}
await saveFiles(locals.user.id, files);
return { success: true };
}
};
The function returned from handleUpload runs when the component unmounts, cancelling in-flight uploads. The server double-checks file count (and should check size) to block malicious requests.
Why it matters
Large uploads often fail. Without progress or cancel options, users close the tab in confusion. Clear states and retry paths reduce support pings for assignment portals or contest submissions.
Practice tasks
- Follow along: use
use:enhance={handleUpload}to build a multi-file form and show the progress bar. - Extend it: add cancel/retry buttons that call
xhr.abort()and reset the UI. - Debugging: if progress is stuck at 0%, verify
event.lengthComputableand theenctypeattribute. - Done when: up to five files upload successfully and retries work after failure.
Wrap-up
Once the upload flow is solid, users feel safe even while waiting. Next we'll review component accessibility and responsive navigation.
💬 댓글
이 글에 대한 의견을 남겨주세요