Загрузка нескольких файлов на Amazon S3

#javascript #ajax #amazon-web-services #amazon-s3

#javascript #ajax #amazon-веб-сервисы #amazon-s3

Вопрос:

У меня есть веб-страница, и пока она работает для загрузки одного файла, но мне нужно иметь возможность загружать несколько файлов одновременно. Amazon это не нравится, и он выдает мне «Для отправки POST требуется ровно одна загрузка файла на запрос». Я бы предположил, что мне просто нужно сделать отдельный запрос POST для каждого файла, но я не знаю, как это сделать. Я просто использую простой HTML и ajax и хотел бы придерживаться этого.

 @import " '_normalize.css' ";
@import " '_defaults.css' ";
body,
.ad,
.sm {
  font-family: Lucida Grande, Helvetica Neue, Helvetica, Arial, Verdana, sans-serif
}

a {
  color: currentColor;
  text-decoration: none
}

.clearfix::after {
  content: '';
  display: table;
  clear: both
}

.ad {
  width: 9.375rem;
  color: #444;
  color: rgba( 0, 0, 0, .75);
  background-color: #fff;
  background-color: rgba( 255, 255, 255, .5);
  position: fixed;
  z-index: 1000;
  top: .625rem;
  left: .625rem;
  padding: .5rem .625rem
}

.ad--dark {
  color: #ddd;
  color: rgba( 255, 255, 255, .75);
  background-color: #111;
  background-color: rgba( 0, 0, 0, .5)
}

.ad__close {
  width: .625rem;
  height: .625rem;
  background-color: #777;
  background-color: rgba( 0, 0, 0, .5);
  border-radius: 50%;
  position: absolute;
  z-index: 1;
  top: -.25rem;
  right: -.25rem;
  -webkit-transition: -webkit-transform .25s ease-in-out;
  transition: transform .25s ease-in-out
}

.ad--dark .ad__close {
  background-color: #ddd;
  background-color: rgba( 255, 255, 255, .75)
}

.ad__close:hover,
.ad__close:focus {
  -webkit-transform: scale( 1.25);
  -ms-transform: scale( 1.25);
  transform: scale( 1.25)
}

#carbonads {
  font-size: .875rem;
  letter-spacing: -.071em;
  line-height: 1.125rem
}

#carbonads a {
  color: currentColor;
  display: block;
  margin-top: .313rem
}

#carbonads .carbon-poweredby {
  font-size: .75rem;
  text-transform: uppercase;
  color: #aaa;
  color: rgba( 0, 0, 0, .25)
}

.ad--dark #carbonads .carbon-poweredby {
  color: #999;
  color: rgba( 255, 255, 255, .25)
}

.sm {
  width: 100%;
  height: 2.5rem;
  color: #444;
  color: rgba( 0, 0, 0, .75);
  background-color: #fff;
  background-color: rgba( 255, 255, 255, .5);
  overflow: hidden;
  position: fixed;
  z-index: 1001;
  bottom: 0;
  left: 0;
  padding: .625rem 1.25rem 0
}

.sm--dark {
  color: #ddd;
  color: rgba( 255, 255, 255, .75);
  background-color: #111;
  background-color: rgba( 0, 0, 0, .5)
}

.sm ul {}

.sm li {
  float: right;
  margin-left: 1rem
}

.sm li:first-child {
  float: left;
  margin-left: 0
}

.sm .googleplus-one {
  max-width: 60px
}

.sm .twitter-follow>*:not( :first-child),
.sm .twitter-share>*:not( :first-child) {
  display: none
}

@media screen {
  @media(min-width: 0px) {
    .sm li:last-child {
      opacity:0;
      -webkit-transition: opacity .25s ease-in-out;
      transition: opacity .25s ease-in-out
    }
    .sm:hover li:last-child {
      opacity: 1
    }
  }
}

.sm__back {
  font-size: .875rem;
  font-weight: 700;
  color: currentColor;
  text-transform: uppercase;
  position: relative
}

.sm__back::before {
  width: 0;
  height: 0;
  border: .313rem solid transparent;
  border-left: none;
  border-right-color: currentColor;
  content: '';
  display: inline-block;
  position: relative;
  left: 0;
  margin-right: .313rem;
  -webkit-transition: left .25s ease-in-out;
  transition: left .25s ease-in-out
}

.sm__back:hover::before,
.sm__back:focus::before {
  left: -.313rem
}

@media screen and (max-width:40em),
screen and (max-height:40em) {
  .ad,
  .sm {
    display: none
  }
}  
 <!DOCTYPE html>
<html lang="en" class="no-js">

<head>
  <meta charset="utf-8">
  <title>TEST</title>
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <link rel="stylesheet" href="main.css" />
  <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,400" />
  <style>
    html {}
    
    body {
      font-family: Roboto, sans-serif;
      color: #0f3c4b;
      background-color: #e5edf1;
      padding: 5rem 1.25rem;
      /* 80 20 */
    }
    
    .container {
      width: 100%;
      max-width: 680px;
      /* 800 */
      text-align: center;
      margin: 0 auto;
    }
    
    .container h1 {
      font-size: 42px;
      font-weight: 300;
      color: #0f3c4b;
      margin-bottom: 40px;
    }
    
    .container h1 a:hover,
    .container h1 a:focus {
      color: #39bfd3;
    }
    
    .container nav {
      margin-bottom: 40px;
    }
    
    .container nav a {
      border-bottom: 2px solid #c8dadf;
      display: inline-block;
      padding: 4px 8px;
      margin: 0 5px;
    }
    
    .container nav a.is-selected {
      font-weight: 700;
      color: #39bfd3;
      border-bottom-color: currentColor;
    }
    
    .container nav a:not( .is-selected):hover,
    .container nav a:not( .is-selected):focus {
      border-bottom-color: #0f3c4b;
    }
    
    .container footer {
      color: #92b0b3;
      margin-top: 40px;
    }
    
    .container footer p p {
      margin-top: 1em;
    }
    
    .container footer a:hover,
    .container footer a:focus {
      color: #39bfd3;
    }
    
    .box {
      font-size: 1.25rem;
      /* 20 */
      background-color: #c8dadf;
      position: relative;
      padding: 100px 20px;
    }
    
    .box.has-advanced-upload {
      outline: 2px dashed #92b0b3;
      outline-offset: -10px;
      -webkit-transition: outline-offset .15s ease-in-out, background-color .15s linear;
      transition: outline-offset .15s ease-in-out, background-color .15s linear;
    }
    
    .box.is-dragover {
      outline-offset: -20px;
      outline-color: #c8dadf;
      background-color: #fff;
    }
    
    .box__dragndrop,
    .box__icon {
      display: none;
    }
    
    .box.has-advanced-upload .box__dragndrop {
      display: inline;
    }
    
    .box.has-advanced-upload .box__icon {
      width: 100%;
      height: 80px;
      fill: #92b0b3;
      display: block;
      margin-bottom: 40px;
    }
    
    .box.is-uploading .box__input,
    .box.is-success .box__input,
    .box.is-error .box__input {
      visibility: hidden;
    }
    
    .box__uploading,
    .box__success,
    .box__error {
      display: none;
    }
    
    .box.is-uploading .box__uploading,
    .box.is-success .box__success,
    .box.is-error .box__error {
      display: block;
      position: absolute;
      top: 50%;
      right: 0;
      left: 0;
      -webkit-transform: translateY( -50%);
      transform: translateY( -50%);
    }
    
    .box__uploading {
      font-style: italic;
    }
    
    .box__success {
      -webkit-animation: appear-from-inside .25s ease-in-out;
      animation: appear-from-inside .25s ease-in-out;
    }
    
    @-webkit-keyframes appear-from-inside {
      from {
        -webkit-transform: translateY( -50%) scale( 0);
      }
      75% {
        -webkit-transform: translateY( -50%) scale( 1.1);
      }
      to {
        -webkit-transform: translateY( -50%) scale( 1);
      }
    }
    
    @keyframes appear-from-inside {
      from {
        transform: translateY( -50%) scale( 0);
      }
      75% {
        transform: translateY( -50%) scale( 1.1);
      }
      to {
        transform: translateY( -50%) scale( 1);
      }
    }
    
    .box__restart {
      font-weight: 700;
    }
    
    .box__restart:focus,
    .box__restart:hover {
      color: #39bfd3;
    }
    
    .js .box__file {
      width: 0.1px;
      height: 0.1px;
      opacity: 0;
      overflow: hidden;
      position: absolute;
      z-index: -1;
    }
    
    .js .box__file label {
      max-width: 80%;
      text-overflow: ellipsis;
      white-space: nowrap;
      cursor: pointer;
      display: inline-block;
      overflow: hidden;
    }
    
    .js .box__file label:hover strong,
    .box__file:focus label strong,
    .box__file.has-focus label strong {
      color: #39bfd3;
    }
    
    .js .box__file:focus label,
    .js .box__file.has-focus label {
      outline: 1px dotted #000;
      outline: -webkit-focus-ring-color auto 5px;
    }
    
    .js .box__file label * {
      /* pointer-events: none; */
      /* in case of FastClick lib use */
    }
    
    .no-js .box__file label {
      display: none;
    }
    
    .no-js .box__button {
      display: block;
    }
    
    .box__button {
      font-weight: 700;
      color: #e5edf1;
      background-color: #39bfd3;
      display: block;
      padding: 8px 16px;
      margin: 40px auto 0;
    }
    
    .box__button:hover,
    .box__button:focus {
      background-color: #0f3c4b;
    }
  </style>


</head>

<body>
  <div class="container" role="main">

    <form action="https://s3-[removed].amazonaws.com/[removed]" method="post" enctype="multipart/form-data" novalidate class="box">
      <input type="hidden" name="key" value="uploads/${filename}">
      <input Access Key ID type="hidden" type="input" name="AWSAccessKeyId" value="[removed]">
      <input Signature type="hidden" type="password" name="signature" value="[removed]">
      <input type="hidden" name="acl" value="private">
      <input type="hidden" name="success_action_redirect" value="[removed]">
      <input type="hidden" name="x-amz-server-side-encryption" value="AES256" />
      <input type="hidden" name="policy" value="[removed]" <!-- Include any additional input fields here -->


      <input type="input" class="text-input" name="x-amz-meta-tag" value="" placeholder="Your Name Here" />


      <div class="box__input">


        <input type="file" name="file" id="file" class="box__file" data-multiple-caption="{count} files selected" multiple />
        <label for="file"><strong>Choose a file</strong><span class="box__dragndrop"> or drag it here</span>.</label>

        <button type="submit" class="box__button">Upload</button>

      </div>
      <div class="box__uploading">Uploadingamp;hellip;</div>
    </form>

  </div>



  <script type="8f8d05b8d77097cd667d97f3-text/javascript">
    'use strict';

    ;
    (function(document, window, index) {
      // feature detection for dragamp;drop upload
      var isAdvancedUpload = function() {
        var div = document.createElement('div');
        return (('draggable' in div) || ('ondragstart' in div amp;amp; 'ondrop' in div)) amp;amp; 'FormData' in window amp;amp; 'FileReader' in window;
      }();


      // applying the effect for every form
      var forms = document.querySelectorAll('.box');
      Array.prototype.forEach.call(forms, function(form) {
        var input = form.querySelector('input[type="file"]'),
          label = form.querySelector('label'),
          errorMsg = form.querySelector('.box__error span'),
          restart = form.querySelectorAll('.box__restart'),
          droppedFiles = false,
          showFiles = function(files) {
            label.textContent = files.length > 1 ? (input.getAttribute('data-multiple-caption') || '').replace('{count}', files.length) : files[0].name;
          },
          triggerFormSubmit = function() {
            var event = document.createEvent('HTMLEvents');
            event.initEvent('submit', true, false);
            form.dispatchEvent(event);
          };

        // letting the server side to know we are going to make an Ajax request
        var ajaxFlag = document.createElement('input');
        ajaxFlag.setAttribute('type', 'hidden');
        ajaxFlag.setAttribute('name', 'ajax');
        ajaxFlag.setAttribute('value', 1);
        form.appendChild(ajaxFlag);

        // automatically submit the form on file select
        input.addEventListener('change', function(e) {
          showFiles(e.target.files);


        });

        // dragamp;drop files if the feature is available
        if (isAdvancedUpload) {
          form.classList.add('has-advanced-upload'); // letting the CSS part to know dragamp;drop is supported by the browser

          ['drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop'].forEach(function(event) {
            form.addEventListener(event, function(e) {
              // preventing the unwanted behaviours
              e.preventDefault();
              e.stopPropagation();
            });
          });
          ['dragover', 'dragenter'].forEach(function(event) {
            form.addEventListener(event, function() {
              form.classList.add('is-dragover');
            });
          });
          ['dragleave', 'dragend', 'drop'].forEach(function(event) {
            form.addEventListener(event, function() {
              form.classList.remove('is-dragover');
            });
          });
          form.addEventListener('drop', function(e) {
            droppedFiles = e.dataTransfer.files; // the files that were dropped
            showFiles(droppedFiles);

          });
        }


        // if the form was submitted
        form.addEventListener('submit', function(e) {
          // preventing the duplicate submissions if the current one is in progress
          if (form.classList.contains('is-uploading')) return false;

          form.classList.add('is-uploading');
          form.classList.remove('is-error');

          if (isAdvancedUpload) // ajax file upload for modern browsers
          {
            e.preventDefault();

            // gathering the form data
            var ajaxData = new FormData(form);
            if (droppedFiles) {
              Array.prototype.forEach.call(droppedFiles, function(file) {
                ajaxData.append(input.getAttribute('name'), file);
              });
            }

            // ajax request
            var ajax = new XMLHttpRequest();
            ajax.open(form.getAttribute('method'), form.getAttribute('action'), true);

            ajax.onload = function() {
              form.classList.remove('is-uploading');
              if (ajax.status >= 200 amp;amp; ajax.status < 400) {
                var data = JSON.parse(ajax.responseText);
                form.classList.add(data.success == true ? 'is-success' : 'is-error');
                if (!data.success) errorMsg.textContent = data.error;
              } else alert(ajax.response);
            };

            ajax.onerror = function() {
              form.classList.remove('is-uploading');
            };

            ajax.send(ajaxData);
          } else // fallback Ajax solution upload for older browsers
          {
            var iframeName = 'uploadiframe'   new Date().getTime(),
              iframe = document.createElement('iframe');

            $iframe = $('<iframe name="'   iframeName   '" style="display: none;"></iframe>');

            iframe.setAttribute('name', iframeName);
            iframe.style.display = 'none';

            document.body.appendChild(iframe);
            form.setAttribute('target', iframeName);

            iframe.addEventListener('load', function() {
              var data = JSON.parse(iframe.contentDocument.body.innerHTML);
              form.classList.remove('is-uploading')
              form.classList.add(data.success == true ? 'is-success' : 'is-error')
              form.removeAttribute('target');
              if (!data.success) errorMsg.textContent = data.error;
              iframe.parentNode.removeChild(iframe);
            });
          }
        });


        // restart the form if has a state of error/success
        Array.prototype.forEach.call(restart, function(entry) {
          entry.addEventListener('click', function(e) {
            e.preventDefault();
            form.classList.remove('is-error', 'is-success');
            input.click();
          });
        });

        // Firefox focus bug fix for file input
        input.addEventListener('focus', function() {
          input.classList.add('has-focus');
        });
        input.addEventListener('blur', function() {
          input.classList.remove('has-focus');
        });

      });
    }(document, window, 0));
  </script>
  <script src="https://ajax.cloudflare.com/cdn-cgi/scripts/7089c43e/cloudflare-static/rocket-loader.min.js" data-cf-settings="8f8d05b8d77097cd667d97f3-|49" defer=""></script>
</body>

<div class="background"></div>

</html>  

Комментарии:

1. Да, вам либо нужно создать несколько форм (что вы можете сделать, программно создав эти формы), либо PUT вместо этого использовать запрос.

2. @Brad Возможно, я просто не знаю, что я делаю, но, похоже, я не могу заставить его отправить как PUT