const needle = require('needle');
const fs = require('fs');
const path = require('path');
const [, scriptName, filename, uploadURL] = process.argv;
if (!filename || !uploadURL) {
console.error(`Usage: node ${path.basename(scriptName)} filename upload_url`);
process.exit(-1);
}
async function uploadFile(file, url) {
// rangeEnd is the index of the last byte in the file, i.e. number of bytes in file
const rangeEnd = (await fs.promises.stat(file)).size;
let options = {
headers: {
'Content-Range': `bytes */${rangeEnd}`,
},
};
const response = await needle('put', url, null, options);
switch (response.statusCode) {
case 200:
case 201:
console.log('Upload complete');
return;
case 308:
return resumeUpload(response, file, url);
default:
console.log('Got unexpected response code: ', response.statusCode);
return;
}
}
async function resumeUpload(response, file, url) {
console.log('Upload not completed, resuming');
if (response.headers.range) {
let resumeOffset = Number(response.headers.range.split('-')[1]) + 1;
let options = {
headers: {
'Content-Range': `bytes ${resumeOffset}-${rangeEnd-1}/${rangeEnd}`,
'Content-Length': `${rangeEnd-resumeOffset}`,
},
};
let readStream = fs.createReadStream(file, {start: resumeOffset});
return needle('put', url, readStream, options);
} else {
console.log('Initiating upload');
let options = {
headers: {
'Content-Type': 'text/plain'
}
};
let readStream = fs.createReadStream(file);
return needle('put', url, readStream, options);
}
}
// Request resumable session URL
async function requestResumableSession(url) {
const options = {
headers: {
'Content-Type': 'text/plain',
'Content-Length': '0',
'x-goog-resumable': 'start',
},
};
const res = await needle('post', url, null, options);
if (res.statusCode === 201) {
const resumableSessionURL = res.headers['location'];
console.log('Starting upload to: ', resumableSessionURL);
await uploadFile(filename, resumableSessionURL);
} else {
console.log('Failed to create resumable session URI');
}
}
requestResumableSession(uploadURL).then(result => console.log('Upload complete'));