UNPKG

@rapidimages/storage-api-client

Version:
234 lines (214 loc) 6.41 kB
'use strict'; var fs = require('fs'); var FormData = require('form-data'); var once = require('once'); var fetch = require('node-fetch'); var crypto = require('crypto'); var concat = require('concat-stream'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var FormData__default = /*#__PURE__*/_interopDefaultLegacy(FormData); var once__default = /*#__PURE__*/_interopDefaultLegacy(once); var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch); var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto); var concat__default = /*#__PURE__*/_interopDefaultLegacy(concat); function progress (files) { let offset = 0; const offsets = files.map(x => { offset += x.size; return { name: x.name, offset: offset } }); return onProgress function onProgress (e) { const percentage = (e.loaded / e.total) * 100; const file = offsets.filter(x => e.loaded <= x.offset)[0] || offsets.slice(-1)[0]; return { percentage: percentage, total: e.total, loaded: e.loaded, totalMB: (e.total / 1e6).toFixed(2) + ' MB', uploadedMB: (e.loaded / 1e6).toFixed(2) + ' MB', file: file ? file.name : '' } } } function onProgress (e) { const percentage = (e.loaded / e.total) * 100; return { file: e.file, total: e.total, loaded: e.loaded, percentage } } var defaults = opts => Object.assign( { onUploadProgress () {}, onHashProgress () {}, onUnknown () {}, onRequest () {} }, opts ); const debug = require('debug')('@rapidimages/storage-api-client'); var index = url => { if (!url) throw new Error('url must be specified') return { upload } function upload (files = [], opts) { const { onUploadProgress, onHashProgress, onUnknown, onRequest } = defaults( opts ); files = files.map(file => (file.path ? file.path : file)); return new Promise((resolve, reject) => { if (!files.length) reject(new Error('no files specified')); getKeys(files, onHashProgress, (err, keys) => { if (err) return reject(err) getUnknownKeys(url, keys, (err, unknown) => { if (err) return reject(err) onUnknown(unknown); getStats(files, (err, stats) => { if (err) return reject(err) const form = new FormData__default['default'](); files.forEach(file => { if (unknown[file]) { form.append(file, fs__default['default'].createReadStream(file)); } else { debug(`file ${file} already known sending details only`); form.append( file, JSON.stringify({ name: file, key: keys[file], size: stats[file].size }) ); } }); const progress$1 = progress( Object.keys(stats).reduce((sum, key) => { if (unknown[key]) { sum.push({ size: stats[key].size, name: key }); } return sum }, []) ); const request = form.submit(`${url}/upload`, (err, res) => { if (err || res.statusCode !== 200) { return reject(new Error(`failed to upload ${err}`)) } res.pipe(concat__default['default'](key => resolve(key))); }); onRequest(request); let total; let loaded = 0; const pendingProgress = []; form.on('error', reject); form.on('data', data => { loaded += data.length; if (total) { onUploadProgress(progress$1({ loaded, total })); } else { pendingProgress.push(loaded); } }); form.getLength((err, length) => { if (!err) { total = length; pendingProgress.forEach(loaded => onUploadProgress(progress$1({ loaded, total })) ); } }); }); }); }); }) } }; function getStats (files, cb) { let pending = files.length; const stats = {}; files.forEach(file => { fs__default['default'].stat(file, (err, stat) => { if (err) return cb(err) stats[file] = stat; done(); }); }); function done () { if (!--pending) { debug('gettings stats for files %j got %j', files, stats); cb(null, stats); } } } function getKeys (files, onHashProgress, cb) { cb = once__default['default'](cb); let loaded = 0; const keys = {}; files.forEach(file => { progress(file); const sha1 = crypto__default['default'].createHash('sha1'); sha1.setEncoding('hex'); fs__default['default'].createReadStream(file) .on('data', sha1.write.bind(sha1)) .on('end', () => { sha1.end(); keys[file] = sha1.read(); done(); }) .on('err', cb); }); function progress (file) { onHashProgress( onProgress({ file, total: files.length, loaded: loaded }) ); } function done (file) { loaded++; progress(file); if (loaded === files.length) { debug('gettings keys for files %j got %j', files, keys); cb(null, keys); } } } function getUnknownKeys (url, details, cb) { cb = once__default['default'](cb); const keys = Object.keys(details).map(x => details[x]); fetch__default['default'](`${url}/unknown`, { method: 'POST', redirect: 'manual', body: JSON.stringify(keys) }) .then(checkStatus) .then(res => res.json()) .then(data => { const unknown = {}; data.forEach(key => { const file = Object.keys(details).filter(x => details[x] === key)[0]; unknown[file] = key; }); debug('checking unknown %j and got %j', keys, unknown); cb(null, unknown); }) .catch(cb); } function checkStatus (res) { return res.status === 200 ? Promise.resolve(res) : res.text().then(text => Promise.reject(text)) } module.exports = index;