UNPKG

@kvass/storage

Version:
154 lines (129 loc) 3.82 kB
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 }