example/evaporate_example.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Evaporate Example</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.css" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="assets/example.css">
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/aws-sdk/2.22.0/aws-sdk.min.js"></script>
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.1.3/js.cookie.js"></script>
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/progressbar.js/1.0.1/progressbar.min.js"></script>
<script language="javascript" type="text/javascript" src="../evaporate.js"></script>
</head>
<body>
<div class="container-fluid">
<h1 id="header">EvaporateJS Example</h1>
<dl>
<dt>AWS Key</dt>
<dd><input id="awsKey" type="text" placeholder="AWS Key"/></dd>
<dt>S3 Bucket</dt>
<dd><input id="s3Bucket" type="text" placeholder="S3 Bucket"/></dd>
<dt id="signerLabel">Signer Url</dt>
<dd><input id="signerUrl" type="text" placeholder="Signer URL"/></dd>
<dt>Signing</dt>
<dd><label><input id="signingMethod" type="checkbox"/> Use unsafe JavaScript custom auth method</label></dd>
<dt class="awsRegion">AWS Region</dt>
<dd class="awsRegion"><input id="awsRegion" type="text" placeholder="us-east-1"/></dd>
<dt>Persist values</dt>
<dd class="cookie">
<label><input name="persist" type="radio" value="off" checked="true"/> No</label>
<label><input name="persist" type="radio" value="1d"/> 1 Day</label>
</dd>
<div class="errors"></div>
</dl>
<input type="file" id="files" multiple />
<button type="button" id="pause-all" class="btn btn-warning btn-sm glyphicon glyphicon-pause" title="Pause All"></button>
<button type="button" id="pause-all-force" class="btn btn-primary btn-sm glyphicon glyphicon-off" title="Force Pause All"></button>
<button type="button" id="resume" class="btn btn-success btn-sm glyphicon glyphicon-play" title="Resume All"></button>
<button type="button" id="cancel-all" class="btn btn-danger btn-sm glyphicon glyphicon-stop" title="Cancel All"></button>
<div><br/>Total parts in-progress: <span id="totalParts">0</span></div>
<div id="progress-container"></div>
</div>
<script language="javascript">
var files, file_id = 0, file_ids = [];
var filePromises = [], allCompleted
COOKIE = 'evaporate_example',
cookie_data = { persist: "off" },
cookie_options = { expires: 1 };
// Change these to reflect your local settings
var persist = $("input[name=persist]").val();
if (persist) {
try {
cookie_data = JSON.parse(Cookies.get(COOKIE) || '{ "persist": "off" }');
$("input[type=radio][name=persist][value=" + cookie_data.persist + "]").attr("checked", true);
updateSignerUi(cookie_data.useUnsafeJavaScript);
$("#awsKey").val(decodeURIComponent(cookie_data.awsKey || ''));
$("#awsRegion").val(decodeURIComponent(cookie_data.awsRegion || ''));
$("#s3Bucket").val(decodeURIComponent(cookie_data.s3Bucket || ''));
$("#signerUrl").val(decodeURIComponent(cookie_data.signerUrl || ''));
} catch (e) {
console.log(e);
}
}
var customAuth = $("#signingMethod")[0].checked;
Evaporate.create({
signerUrl: customAuth ? undefined : $("#signerUrl").val(),
aws_key: $("#awsKey").val(),
awsRegion: ($("#awsRegion").val() || '').trim() || "us-east-1",
bucket: $("#s3Bucket").val(),
cloudfront: true,
computeContentMd5: true,
cryptoMd5Method: function (data) { return AWS.util.crypto.md5(data, 'base64'); },
cryptoHexEncodedHash256: function (data) { return AWS.util.crypto.sha256(data, 'hex'); },
logging: false,
s3FileCacheHoursAgo: 1,
allowS3ExistenceOptimization: true,
customAuthMethod: customAuth? doNotUseUnsafeJavaScriptV4Signer : undefined,
evaporateChanged: function (file, evaporatingCount) {
$('#totalParts').text(evaporatingCount);
if (evaporatingCount > 0) {
$("#pause-all, #pause-all-force, #cancel-all").show();
} else if (evaporatingCount === 0) {
$("#pause-all, #pause-all-force, #resume, #cancel-all").hide();
}
}
})
.then(function (_e_) {
$('#files').change(function (evt) {
files = evt.target.files;
for (var i = 0; i < files.length; i++) {
var name = files[i].name + Math.random() * 100;
var fileKey = $("#s3Bucket").val() + '/' + name;
callback_methods = callbacks(files[i], fileKey, i);
var promise = _e_.add({
name: name,
file: files[i],
started: callback_methods.started,
complete: callback_methods.complete,
cancelled: callback_methods.cancelled,
progress: callback_methods.progress,
error: callback_methods.error,
warn: callback_methods.warn,
paused: callback_methods.paused,
pausing: callback_methods.pausing,
resumed: callback_methods.resumed,
nameChanged: callback_methods.nameChanged
},
{
bucket: $("#s3Bucket").val(), // Shows that the bucket can be changed per
aws_key: $("#awsKey").val() // Shows that aws_key can be changed per
}
)
.then((function (requestedName) {
return function (awsKey) {
if (awsKey === requestedName) {
console.log(awsKey, 'successfully uploaded!');
} else {
console.log('Did not re-upload', requestedName, 'because it exists as', awsKey);
}
}
})(name)
);
filePromises.push(promise);
callback_methods.progress_clock.attr('file_id', file_id);
["#pause-all", "#pause-all-force", "#cancel-all"].forEach(function (v) { $(v).show(); });
}
allCompleted = Promise.all(filePromises)
.then(function () {
console.log('All files were uploaded successfully.');
}, function (reason) {
console.log('All files were not uploaded successfully:', reason);
})
$(evt.target).val('');
});
$("#pause-all").hide().click(function () {
_e_.pause();
});
$("#cancel-all").hide().click(function () {
_e_.cancel();
});
$("#pause-all-force").hide().click(function () {
_e_.pause(undefined, {force: true});
});
$("#resume").hide().click(function () {
_e_.resume();
$("#resume").hide();
});
function callbacks(file, fileKey, idx) {
var progress_clock = $('<div class="progress-clock"/>'),
clock,
progress,
file_id;
$('#progress-container')
.append(progress_clock);
progress_clock
.append('<span>' + file.name + '</span>')
.append('<div class="circle"/>');
var cancel = $('<button class="cancel btn btn-danger btn-xs glyphicon glyphicon-stop" title="Cancel"></button>')
.click(function () {
console.log('canceling', fileKey);
_e_.cancel(fileKey);
});
progress_clock.append(cancel);
var pause = $('<button class="pause btn btn-warning btn-xs glyphicon glyphicon-pause" title="Pause"></button>')
.click(function () {
console.log('pausing', fileKey);
_e_.pause(fileKey);
});
progress_clock.append(pause);
var forcePause = $('<button class="pause btn btn-primary btn-xs glyphicon glyphicon glyphicon-off" title="Force Pause"></button>')
.click(function () {
console.log('force pausing', fileKey);
_e_.pause(fileKey, {force: true});
});
progress_clock.append(forcePause);
var resume = $('<button class="resume btn btn-success btn-xs glyphicon glyphicon-play" title="Resume"></button>').hide()
.hide()
.click(function () {
console.log('resuming', fileKey);
_e_.resume(fileKey);
});
progress_clock.append(resume);
var status = $('<span class="status"></span>');
progress_clock.append(status);
var speed = $('<span class="speed">786 Kbs</span>');
progress_clock.append(speed);
clock = new ProgressBar.Circle(progress_clock.find('.circle')[0], {
strokeWidth: 3,
trailWidth: 1,
duration: 350,
text: {
value: ''
},
step: function(state, bar) {
bar.setText((bar.value() * 100).toFixed(0) + '%');
}
});
progress_clock.find('svg path').removeAttr('stroke');
progress_clock.find('.progressbar-text').css('color', '');
function markComplete(className) {
progress_clock.addClass(className);
status.text(className);
}
return {
progress: function (progressValue, data) {
progress = progressValue;
console.log(
'Total Loaded:', data && data.loaded ? data.loaded : '',
'Speed:', data && data.speed ? data.speed : '',
'Formatted speed:', data && data.speed ? data.readableSpeed + 's' : '',
'Minutes left:', data && data.secondsLeft ? Math.round(data.secondsLeft / 60) : '')
clock.animate(progressValue);
if(data) {
var xferRate = data.speed ? '<br />' + data.readableSpeed + "s" : '',
remaining = data.secondsLeft ? '<br />' + Math.round(data.secondsLeft / 60) + 'm left' : '';
speed.html(xferRate + remaining);
}
},
started: function (fid) {
console.log('started', fid)
file_id = fid;
pause.show();
forcePause.show();
resume.hide();
progress_clock.addClass('evaporating');
status.text('evaporating');
},
error: function (msg) {
var m = $('<div/>').append(msg);
var html = $('<small/>').html(m);
markComplete('error');
clock.animate(progress);
progress_clock.removeClass('evaporating warning');
},
cancelled: function () {
clock.animate(progress);
markComplete('canceled');
progress_clock.removeClass('evaporating warning paused pausing');
cancel.hide();
resume.hide();
pause.hide();
forcePause.hide();
},
pausing: function () {
clock.animate(progress);
markComplete('pausing');
$("#resume").show();
pause.hide();
forcePause.hide();
progress_clock.removeClass('evaporating warning');
},
paused: function () {
clock.animate(progress);
markComplete('paused');
pause.hide();
forcePause.hide();
resume.show();
$("#resume").show();
progress_clock.removeClass('evaporating warning pausing');
},
resumed: function () {
clock.animate(progress);
markComplete('');
resume.hide();
progress_clock.removeClass('pausing paused');
},
warn: function (msg) {
var m = $('<small/>').html(msg);
var html = $('<div/>').append(m);
clock.animate(progress)
},
nameChanged: function (awsKey) {
console.log('Evaporate will use existing S3 upload for', awsKey,
'rather than the requested object name', file_id)
},
complete: function (_xhr, awsKey, stats){
var m = $('<small/>').html(awsKey + ' - Completed');
var html = $('<div/>').append(m);
clock.animate(1);
progress_clock.removeClass('evaporating warning');
markComplete('completed');
console.log('Stats for', decodeURIComponent(awsKey), stats);
},
progress_clock: progress_clock
}
}
},
function (reason) {
$("div.errors").html('Evaporate failed to initialize: ' + reason + '. Change parameters and refresh page.');
});
$(document).ready(function() {
$("#signingMethod").change(function () { updateSignerUi(this.checked); });
$("input[type=text]").change(function () {
cookie_data[this.id] = this.value;
setDevCookie();
});
$("input[type=radio][name=persist], #signingMethod").change(setDevCookie);
function setDevCookie() {
Cookies.remove(COOKIE, cookie_options);
var v = $("input[type=radio][name=persist]:checked").val();
if (v === "off") { return; }
cookie_data.persist = v;
cookie_data.useUnsafeJavaScript = $("#signingMethod")[0].checked;
cookie_data.awsKey = encodeURIComponent($("#awsKey").val().trim());
cookie_data.awsRegion = encodeURIComponent($("#awsRegion").val().trim());
cookie_data.s3Bucket = encodeURIComponent($("#s3Bucket").val().trim());
cookie_data.signerUrl = encodeURIComponent($("#signerUrl").val().trim());
Cookies.set(COOKIE, JSON.stringify(cookie_data), cookie_options);
}
});
function updateSignerUi(checked) {
$("#signingMethod")[0].checked = checked;
$("#signerLabel").html(checked ? "AWS Secret" : "Signer URL");
$("#signerUrl").attr('placeholder', checked ? "AWS Secret" : "Signer URL");
$(".awsRegion")[checked ? 'show' : 'hide']();
}
function doNotUseUnsafeJavaScriptV4Signer(_signParams, _signHeaders, stringToSign, dateString) {
// http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
// DO NOT USE JavaScript to sign requests as it risks exposing your AWS secret
// This method is provided for development testing and learning
return new Promise(function (resolve, reject) {
var hmac = function (k, v) { return AWS.util.crypto.hmac(k, v, 'buffer'); },
awsSecret = $("#signerUrl").val().trim(),
awsRegion = ($("#awsRegion").val() || '').trim() || "us-east-1",
date = hmac(["AWS4", awsSecret].join(""), dateString.substr(0, 8)),
region = hmac(date, awsRegion),
service = hmac(region, "s3"),
signing = hmac(service, "aws4_request"),
signingKey = AWS.util.crypto.hmac(signing, decodeURIComponent(stringToSign), 'hex');
resolve(signingKey);
});
}
</script>
</body>
</html>