in public/explorer.js [966:1227]
function UploadController($scope, SharedService) {
DEBUG.log('UploadController init');
window.uploadScope = $scope; // for debugging
$scope.upload = {
button: null, title: null, files: [], uploads: [],
};
// Cache jquery selectors
const $btnUpload = $('#upload-btn-upload');
const $btnCancel = $('#upload-btn-cancel');
// Add new click handler for Cancel button
$btnCancel.click((e2) => {
e2.preventDefault();
$scope.upload.uploads.forEach(upl => upl.abort());
});
//
// Upload a list of local files to the provided bucket and prefix
//
$scope.uploadFiles = (Bucket, prefix) => {
$scope.$apply(() => {
$scope.upload.uploads = [];
$scope.upload.uploading = true;
});
DEBUG.log('Dropped files:', $scope.upload.files);
$scope.upload.files.forEach((file, ii) => {
DEBUG.log('File:', file);
DEBUG.log('Index:', ii);
$(`#upload-td-${ii}`).html(
`<div class="progress"><span id="upload-td-progress-${ii}" class="progress-bar" data-percent="0">0%</span></div>`,
);
const s3 = new AWS.S3(AWS.config);
const params = {
Body: file.file, Bucket, Key: (prefix || '') + (file.file.fullPath ? file.file.fullPath : file.file.name), ContentType: file.file.type,
};
const funcprogress = (evt) => {
DEBUG.log('Part:', evt.part, evt.loaded, evt.total);
const pc = evt.total ? ((evt.loaded * 100.0) / evt.total) : 0;
const pct = Math.round(pc);
const pcts = `${pct}%`;
const col = $(`#upload-td-progress-${ii}`);
col.attr('data-percent', pct);
col.css('width', pcts).text(pcts);
};
const funcsend = (err, data) => {
if (err) {
// AccessDenied is a normal consequence of lack of permission
// and we do not treat this as completely unexpected
if (err.code === 'AccessDenied') {
$(`#upload-td-${ii}`).html('<span class="uploaderror">Access Denied</span>');
} else if (err.code === 'RequestAbortedError') {
DEBUG.log(err.message);
$btnUpload.hide();
$btnCancel.text('Close');
SharedService.viewRefresh();
} else {
DEBUG.log(JSON.stringify(err));
$(`#upload-td-${ii}`).html(`<span class="uploaderror">Failed: ${err.code}</span>`);
SharedService.showError(params, err);
}
} else {
DEBUG.log('Uploaded', file.file.name, 'to', data.Location);
let count = $btnUpload.attr('data-filecount');
$btnUpload.attr('data-filecount', --count);
$(`#upload-td-progress-${ii}`).addClass('progress-bar-success');
$scope.$apply(() => {
$scope.upload.button = `Upload (${count})`;
});
// If all files uploaded then refresh underlying folder view
if (count === 0) {
$btnUpload.hide();
$btnCancel.text('Close');
SharedService.viewRefresh();
}
}
};
const upl = s3.upload(params);
$scope.$apply(() => {
$scope.upload.uploads.push(upl);
});
upl.on('httpUploadProgress', funcprogress).send(funcsend);
});
};
// Wrap readEntries in a promise to make working with readEntries easier
async function readEntriesPromise(directoryReader) {
try {
return await new Promise((resolve, reject) => {
directoryReader.readEntries(resolve, reject);
});
} catch (err) {
DEBUG.log(err);
return undefined;
}
}
// Get all the entries (files or sub-directories) in a directory
// by calling readEntries until it returns empty array
async function readAllDirectoryEntries(directoryReader) {
const entries = [];
let readEntries = await readEntriesPromise(directoryReader);
while (readEntries.length > 0) {
entries.push(...readEntries);
// eslint-disable-next-line no-await-in-loop
readEntries = await readEntriesPromise(directoryReader);
}
return entries;
}
// Retrieve File object from FileEntry
async function filePromise(fileEntry) {
try {
return await new Promise((resolve, reject) => {
fileEntry.file(resolve, reject);
});
} catch (err) {
DEBUG.log(err);
return undefined;
}
}
// Get all files recursively
async function getAllFileEntries(dataTransferItemList) {
const fileEntries = [];
const queue = [];
for (let i = 0; i < dataTransferItemList.length; i++) {
const dtItem = dataTransferItemList[i];
queue.push(typeof dtItem.webkitGetAsEntry === 'function' ? dtItem.webkitGetAsEntry() : dtItem.getAsEntry());
}
while (queue.length > 0) {
const entry = queue.shift();
if (entry) {
if (entry.isFile) {
// eslint-disable-next-line no-await-in-loop
const file = await filePromise(entry);
file.fullPath = entry.fullPath.substring(1);
fileEntries.push(file);
} else if (entry.isDirectory) {
const reader = entry.createReader();
// eslint-disable-next-line no-await-in-loop
queue.push(...await readAllDirectoryEntries(reader));
}
}
}
return fileEntries;
}
// Wrapper to get files safe
async function getFilesList(dataTransfer) {
if (dataTransfer.items.length > 0) {
if (typeof dataTransfer.items[0].webkitGetAsEntry === 'function'
|| typeof dataTransfer.items[0].getAsEntry === 'function') return getAllFileEntries(dataTransfer.items);
DEBUG.log('Can not do folders upload, falling back to files only');
return dataTransfer.files;
} return [];
}
//
// Drag/drop handler for files to be uploaded
//
$scope.dropZone = (target) => {
target
.on('dragover', () => {
target.addClass('dragover');
return false;
})
.on('dragend dragleave', () => {
target.removeClass('dragover');
return false;
})
.on('drop', async (e) => {
DEBUG.log('Dropped files');
e.stopPropagation();
e.preventDefault();
target.removeClass('dragover');
$bl.addClass('fa-spin');
const files = SharedService.hasAddedFiles()
? SharedService.getAddedFiles()
: await getFilesList(e.originalEvent.dataTransfer);
$bl.removeClass('fa-spin');
if (!files.length) {
DEBUG.log('Nothing to upload');
return false;
}
$scope.$apply(() => {
$scope.upload.files = [];
for (let ii = 0; ii < files.length; ii++) {
const fileii = files[ii];
if (fileii.type || fileii.size % 4096 !== 0 || fileii.size > 1048576) {
DEBUG.log('File:', fileii.name, 'Size:', fileii.size, 'Type:', fileii.type);
$scope.upload.files.push({
file: fileii,
name: fileii.fullPath || fileii.name,
type: fileii.type,
size: bytesToSize(fileii.size),
short: path2short(fileii.fullPath || fileii.name),
});
}
}
});
const { bucket } = SharedService.getSettings();
const prefix = SharedService.getViewPrefix();
// Remove any prior click handler from Upload button
$btnUpload.unbind('click');
// Add new click handler for Upload button
$btnUpload.click((e2) => {
e2.preventDefault();
$scope.uploadFiles(bucket, prefix);
});
// Reset buttons for initial use
$btnUpload.show();
$btnCancel.text('Cancel');
// Bind file count into button
$btnUpload.attr('data-filecount', files.length);
$scope.$apply(() => {
$scope.upload.title = `${bucket}/${prefix || ''}`;
$scope.upload.button = `Upload (${files.length})`;
$scope.upload.uploading = false;
});
// Reset files selector
if (SharedService.hasAddedFiles()) {
SharedService.resetAddedFiles();
$('#addedFiles').val('');
}
// Launch the uploader modal
$('#UploadModal').modal({ keyboard: true, backdrop: 'static' });
return false;
});
};
// Enable dropzone behavior and highlighting
$scope.dropZone($('.dropzone'));
// Simulate drop event on change of files selector
$('#addedFiles').on('change', (e) => {
SharedService.addFiles(e.target.files);
$('.dropzone').trigger('drop');
});
}