Skip to content

Instantly share code, notes, and snippets.

@antony
Created September 18, 2019 16:03
Show Gist options
  • Save antony/3da64df9ad44912654391fe3d451e815 to your computer and use it in GitHub Desktop.
Save antony/3da64df9ad44912654391fe3d451e815 to your computer and use it in GitHub Desktop.
File Uploader Svelte
<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