Created
September 18, 2019 16:03
-
-
Save antony/3da64df9ad44912654391fe3d451e815 to your computer and use it in GitHub Desktop.
File Uploader Svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<label> | |
{#if uploading} | |
<Progress bind:percent={progress} text="Uploading..." /> | |
{:else if processing} | |
<Progress percent={100} text="Processing..." /> | |
{:else} | |
<slot name="content"> | |
</slot> | |
{/if} | |
<input | |
bind:this={uploader} | |
type="file" | |
class="visually-hidden" | |
class:busy={uploading} | |
on:change={upload} | |
/> | |
</label> | |
<script> | |
import Progress from '~components/progress/Progress.svelte' | |
import { mimeType } from '@beyonk/components/lib/mime-type' | |
import { createEventDispatcher } from 'svelte' | |
const dispatch = createEventDispatcher() | |
export let endpoint | |
export let destination | |
export let maxFileSize= 3145728 | |
export let allowedFileTypes = ['png', 'jpg', 'jpeg', 'pdf', 'doc', 'docx'] | |
let uploader | |
let uploading = false | |
let processing = false | |
let progress = 0 | |
function reset () { | |
uploading = false | |
processing = false | |
progress = 0 | |
} | |
function getFileExtension (file) { | |
const filenameParts = file.name.split('.') | |
return filenameParts[filenameParts.length - 1].toLowerCase() | |
} | |
function validateUpload (file) { | |
if (file.size > maxFileSize) { | |
dispatch('error', { message: `Maximum file size is ${maxFileSize / (1024 * 1024)}mb` }) | |
return false | |
} | |
if (!allowedFileTypes.includes(getFileExtension(file))) { | |
dispatch('error', { message: `Allowed file types are ${allowedFileTypes.join(',')}.` }) | |
return false | |
} | |
return true | |
} | |
function onError (e) { | |
dispatch('error', { message: `We were unable to upload your photo, ${e.message}. Please try again.` }) | |
} | |
function onProgress (e) { | |
if (e.lengthComputable) { | |
progress = Math.round((e.loaded * 100) / e.total) | |
} | |
} | |
async function upload (attempt = 0) { | |
if (attempt > 5) { | |
onError(new Error('Upload failed after 5 retries. Please try later.')) | |
return | |
} | |
const file = uploader.files[0] | |
const fileMeta = { type: mimeType(getFileExtension(file)) } | |
if (!validateUpload(file)) { | |
return | |
} | |
uploading = true | |
const { url, fields, method } = await endpoint(fileMeta, destination) | |
const reader = new FileReader() | |
const xhr = new XMLHttpRequest() | |
xhr.upload.onprogress = onProgress | |
xhr.onload = () => { | |
uploading = false | |
processing = true | |
dispatch('upload', { ...fields, ...fileMeta }) | |
} | |
xhr.onloadend = reset | |
xhr.onerror = onError | |
xhr.open(method, url, true) | |
xhr.timeout = 15000 | |
xhr.setRequestHeader('Content-Type', fileMeta.type) | |
xhr.upload.addEventListener('timeout', () => { upload(++attempt) }, false) | |
reader.onload = function (evt) { | |
xhr.send(evt.target['result']) | |
} | |
reader.readAsArrayBuffer(file) | |
} | |
</script> | |
<style> | |
.visually-hidden { | |
height: 0.1px; | |
width: 0.1px; | |
opacity: 0; | |
z-index: -1; | |
position: absolute; | |
overflow: hidden; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment