function UploadController()

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:&nbsp${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');
  });
}