@kvass/storage
Version:
Kvass storage engine
154 lines (129 loc) • 3.82 kB
JavaScript
import fetch from './fetch'
import Compressor from 'compressorjs'
const CompressionPreset = {
default: {
maxWidth: 1920,
},
logo: {
maxWidth: 1000,
quality: 0.9,
},
gallery: {
maxWidth: 2560,
},
}
const toArray = v => v instanceof Array ? v : [v]
function Compress(file, options) {
//Compressor.js is designed for image compression; using it on other file types may produce incorrect results
return new Promise((resolve, reject) => {
if (!options) return resolve(file)
new Compressor(file, {
success: res => resolve(res),
error: err => reject(err),
...options,
})
})
}
function Sign(options = {}) {
let { acl = 'public-read', url, ...rest } = options
if (!url) url = '/api/file/sign?acl=' + acl
return fetch(url, rest).then(res => res.json())
}
function Upload({ file, url, onProgress }) {
return new Promise((resolve, reject) => {
let params = new URLSearchParams(url)
let req = new XMLHttpRequest()
req.upload.addEventListener('progress', onProgress)
req.addEventListener('error', reject)
req.addEventListener('load', function () {
resolve(this)
})
req.open('PUT', url)
if (params.get('x-amz-acl')) req.setRequestHeader('x-amz-acl', params.get('x-amz-acl'))
req.send(file)
})
}
function Preview(file) {
return new Promise(resolve => {
let reader = new FileReader()
reader.onload = function (e) {
resolve(e.target.result)
}
reader.readAsDataURL(file)
})
}
function getDimensions(file) {
if (!file.type.startsWith('image/')) return Promise.resolve()
return Preview(file).then(
dataurl =>
new Promise((resolve, reject) => {
let image = new Image()
image.onload = () => resolve([image.naturalWidth, image.naturalHeight])
image.onerror = err => reject(err)
image.src = dataurl
}),
)
}
async function Convert(file, converter) {
switch (typeof converter) {
case "function":
return converter(file)
case "string":
const body = new FormData()
body.append('file', file)
return window.fetch(converter, {
method: 'POST',
credentials: 'include',
body
}).then(res => res.json())
.then(toArray)
.then(data => data.map(({ data, name, type }) => new File([new Uint8Array(data.data)], name, { type })))
default:
return file
}
}
async function VueComponentUpload(rawFile, onProgress, options) {
let {
compression,
transform = v => v,
rename,
convert,
svgConvertEndpoint = '/api/file/transform/png',
...fetchOptions
} = options
if (typeof compression === 'string') compression = CompressionPreset[compression]
//add converter for svg -> png if image compression
if (rawFile.type === 'image/svg+xml' && compression && svgConvertEndpoint) {
convert = svgConvertEndpoint
}
const convertedFiles = await Convert(rawFile, convert).then(toArray)
const result = await Promise.all(
convertedFiles.map(async convertedFile => {
let [sign, file] = await Promise.all([
Sign(fetchOptions || {}),
compression ? Compress(convertedFile, compression) : Promise.resolve(convertedFile),
])
let dimensions
try {
dimensions = await getDimensions(file)
} catch (err) { }
await Upload({
file,
url: sign.uploadUrl,
onProgress(event) {
onProgress(Math.floor((event.loaded / event.total) * 100))
},
})
return transform({
name: rename || file.name,
size: file.size,
url: sign.url,
type: file.type,
dimensions,
})
})
)
if (result.length === 1) return result[0]
return result
}
export { Sign, Upload, Preview, Compress, VueComponentUpload }