uploadFile(file, options) uploads a single file with observable
progress, optional content-addressed dedup, and cancellation. It POSTs
the file via XMLHttpRequest so progress is reportable (fetch
doesn't expose upload progress cross-browser).
When existsUrl is provided, the file's SHA-1 hash is computed and a
HEAD request is issued against existsUrl(hash, file) first — a 2xx
response skips the upload and returns that cached URL. Otherwise the
file is POSTed to endpoint as multipart/form-data under field,
and the (trimmed) response body becomes the result URL unless
parseResponse overrides it. It is the policy-free primitive behind
the upload and dropzone ref
factories.
| Argument | Type | Description |
|---|---|---|
file |
File |
The file to upload. |
options |
UploadFileOptions |
Upload configuration (below). |
options fields:
| Field | Type | Description |
|---|---|---|
endpoint |
string |
URL the file is POSTed to. |
existsUrl |
(hash, file) => string |
Optional. Maps a SHA-1 hash to a URL; a 2xx HEAD there skips the upload (dedup). |
field |
string |
Form field name for the file. Defaults to 'file'. |
parseResponse |
(text, xhr) => string |
Optional. Extracts the result URL from the response. Defaults to the trimmed response text. |
onProgress |
({ file, loaded, total }) => void |
Optional. Fires during the POST; cache hits fire once with loaded === total. |
signal |
AbortSignal |
Optional. Cancels an in-flight upload. |
Returns: Promise<{ url, file, hash? }>. hash is present only
when existsUrl was provided. Cancellation rejects with a
DOMException of name AbortError.
Uploads a file picked from an <input>, logging progress and the
resulting URL. The cancel button aborts an in-flight upload via its
AbortSignal — the promise then rejects with an AbortError.
import { render, signal } from 'pota'
import { uploadFile } from 'pota/use/upload'
function App() {
const status = signal('idle')
let controller
const onChange = async e => {
const file = e.target.files[0]
if (!file) return
controller = new AbortController()
status.write('uploading')
try {
const { url } = await uploadFile(file, {
endpoint: '/api/upload',
onProgress: ({ loaded, total }) =>
status.write(`${Math.round((loaded / total) * 100)}%`),
signal: controller.signal,
})
status.write('done: ' + url)
} catch (err) {
status.write('failed: ' + String(err))
}
}
return (
<>
<input
type="file"
on:change={onChange}
/>
<button on:click={() => controller?.abort()}>cancel</button>
<p>{status.read}</p>
</>
)
}
render(App)
Computes the file's SHA-1 hash and checks existsUrl(hash) with a
HEAD request before uploading; if the bytes are already on the CDN,
the upload is skipped and the cached URL is returned with its hash.
import { uploadFile } from 'pota/use/upload'
const picked = new File(['…'], 'note.txt')
const { url, file, hash } = await uploadFile(picked, {
endpoint: '/api/upload',
existsUrl: hash => '/cdn/' + hash,
field: 'attachment',
parseResponse: (text, xhr) => JSON.parse(text).url,
})