@rapidimages/storage-api-client
Version:
storage api client
234 lines (214 loc) • 6.41 kB
JavaScript
;
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;