scinote-web/app/assets/javascripts/direct-upload.js

213 lines
6.6 KiB
JavaScript
Raw Normal View History

2016-02-12 23:52:43 +08:00
(function (exports) {
var styleOptionRe = /(\d+)x(\d+)/i;
function parseStyleOption(option) {
var m = option.match(styleOptionRe);
return {
width: m && m[1] || 150,
height: m && m[2] || 150
};
}
/*
* Edits (size, quality, parameters) image file for S3 server uploading.
*/
2016-02-12 23:52:43 +08:00
function generateThumbnail(origFile, type, max_width, max_height, cb) {
var fileRequest = $.Deferred();
2016-02-12 23:52:43 +08:00
var img = new Image;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
// todo allow for different x/y ratio
canvas.width = max_width;
canvas.height = max_height;
img.onload = function () {
var size;
var offsetX = 0;
var offsetY = 0;
2016-02-12 23:52:43 +08:00
if (this.width > this.height) {
size = this.height;
offsetX = (this.width - this.height) / 2;
} else {
size = this.width;
offsetY = (this.height - this.width) / 2;
}
if (type === "image/jpeg") {
2016-02-12 23:52:43 +08:00
type = "image/jpg";
}
ctx.drawImage(this, offsetX, offsetY, size, size, 0, 0,
canvas.width, canvas.height);
2016-02-12 23:52:43 +08:00
canvas.toBlob(function (blob) {
fileRequest.resolve(cb(blob));
}, type, 0.8);
2016-02-12 23:52:43 +08:00
};
// Must be after onload, otherwise it could load before we would
// capture the event
img.src = URL.createObjectURL(origFile);
2016-02-12 23:52:43 +08:00
return fileRequest.promise();
}
2016-02-12 23:52:43 +08:00
/*
* The server checks if files are OK (correct file type, presence,
* size and spoofing) and only then generates posts for S3 server file
* uploading (each post for different size/style of the same file).
*/
function fetchUploadSignature(ev, fileInput, file, signUrl) {
var formData = new FormData();
formData.append("file", file);
return $.ajax({
url: signUrl,
type: 'POST',
data: formData,
processData: false,
contentType: false,
error: function (xhr) {
try {
var data = JSON.parse(xhr.responseText);
if (data.status === "error") {
// File error
var errMsg = jsonToValuesArray(data.errors);
renderFormError(ev, fileInput, errMsg);
}
} catch(err) {
// Connection error
var errMsg = I18n.t("general.file.upload_failure");
renderFormError(ev, fileInput, errMsg);
}
}
});
2016-02-12 23:52:43 +08:00
}
/*
* Upload file to S3 server.
*/
function uploadFile(postData, ev, fileInput) {
var url = postData.url;
var fields = postData.fields;
var formData = new FormData();
2016-02-12 23:52:43 +08:00
for (var k in fields) {
formData.append(k, fields[k]);
2016-02-12 23:52:43 +08:00
}
formData.append("file", postData.file, postData.fileName);
2016-02-12 23:52:43 +08:00
return $.ajax({
url: url,
type: 'POST',
data: formData,
processData: false,
contentType: false,
error: function () {
// Connection error
var errMsg = I18n.t("general.file.upload_failure");
renderFormError(ev, fileInput, errMsg);
}
});
2016-02-12 23:52:43 +08:00
}
/*
* Proccesses file's posts and uploads them.
*/
function processPosts(ev, fileInput, posts, fileRequests) {
var file = fileInput.files[0];
_.each(posts, function (postData) {
postData.fileName = file.name;
if (postData.style_option) {
// Picture file
var styleSize = parseStyleOption(postData.style_option);
var fileRequest = generateThumbnail(file, postData.mime_type, styleSize.width,
styleSize.height, function (blob) {
postData.file = blob;
uploadFile(postData, ev, fileInput);
});
} else {
// Other file
postData.file = file;
var fileRequest = uploadFile(postData, ev, fileInput);
}
fileRequests.push(fileRequest);
});
}
/*
* Validates files on server and uploads them to S3 server.
*
* First we asyncronously validate files on server and generate post requests
* (fetchUploadSignature), if OK the post requests are used to uplaod files
* asyncronously to S3 (uploadFile), and if successful the form is submitted,
* otherwise no file is saved and errors are shown.
* If any post fails, the user is allowed to leave the page, but other files
* are still being uploaded because of asynchronous behaviour, so that errors
* for other files can still show afterwards.
*
* TODO On S3 server upload error the other files that were already
* asynchronously uploaded remain on the server, but should be deleted - this
* should generally not happen, because all files should fail to upload in
* such cases (like S3 server connection error), except if user cancels the
* upload while still in progress. But this can be abused! One way to solve
* it is to make request to our server for each file seperatelly, and not for
* all together as it is now, despite being less efficient. To make it
* bulletproof, post requests should be issued on server-side.
*/
exports.directUpload = function (ev, signUrl) {
$form = $(ev.target.form);
$form.clearFormErrors();
$form.removeBlankFileForms();
$fileInputs = $form.find("input[type=file]");
var signRequests = [];
if ($fileInputs.length) {
// Before file processing and uploading
animateSpinner();
preventLeavingPage(true, I18n.t("general.file.uploading"));
ev.preventDefault();
// Validates files and if OK gets upload post requests
_.each($fileInputs, function (fileInput) {
var file = fileInput.files[0];
if (!_.isUndefined(file)) {
signRequests.push(
fetchUploadSignature(ev, fileInput, file, signUrl)
);
}
});
$.when.apply($, signRequests).then(function () {
// After successful file validation and posts fetching
if (signRequests.length) {
var fileRequests = [];
2016-02-12 23:52:43 +08:00
if (signRequests.length == 1) {
arguments = [arguments];
}
$.each(arguments, function (fileIdx, responseData) {
$fileInput = $fileInputs[fileIdx];
var jqXHR = responseData[2];
var data = JSON.parse(jqXHR.responseText);
processPosts(ev, $fileInput, data.posts, fileRequests);
});
2016-02-12 23:52:43 +08:00
$.when.apply($, fileRequests).always(function () {
animateSpinner(null, false);
preventLeavingPage(false);
}).then(function () {
// After successful posts processing and file uploading
$form.submit();
});
}
}, function () {
// After unsuccessful file validation and posts fetching
animateSpinner(null, false);
preventLeavingPage(false);
});
}
2016-07-21 19:11:15 +08:00
};
2016-02-12 23:52:43 +08:00
}(this));