1

Info: I want to upload multiple file using tus-js-client. I am facing some issue when i try to upload multiple files.

Issues:

  • If i try to upload multiple files The progress bar is fluctuating because index of chunks are not properly so that's why the progress bar is fluctuating.

  • I want each file upload next file start after first file complete.

const fileInput = document.querySelector('#js-file-input')
    const dataDiv = document.querySelector('#data-pre');
    const toggleBtn = document.querySelector('#toggle-btn')
    const progressBar = document.querySelector('.progress-bar')
    const textProgress = document.querySelector('#js-upload-text-progress')
    const uploadList = document.querySelector('#upload-list')

    function reset() {
      progressBar.style.width = `${0}%`
      textProgress.textContent = ""
    }
    /**
   * Turn a byte number into a human readable format.
   * Taken from https://stackoverflow.com/a/18650828
   */
    function formatBytes(bytes, decimals = 2) {
      if (bytes === 0) return '0 Bytes'

      const k = 1024
      const dm = decimals < 0 ? 0 : decimals
      const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
      const i = Math.floor(Math.log(bytes) / Math.log(k))
      return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
    }

    fileInput.addEventListener('change', (e) => {
      const files = e.target.files;
      let convertedFiles = [];

      Object.keys(files).forEach(k => {
        convertedFiles = [
          ...convertedFiles,
          { id: URL.createObjectURL(files[k]), file: files[k] }
        ];
      });

      convertedFiles.map(i => {
        const file = i.file;
        const chunkSize = parseInt(5242880, 10)

        let upload = new tus.Upload(file, {
          endpoint: "https://tusd.tusdemo.net/files/",
          // endpoint: "http://localhost:8000/tus/upload/",
          chunkSize,
          retryDelays: [0, 1000, 3000, 5000],
          metadata: {
            filename: file.name,
            filetype: file.type
          },
          onError: function (error) {
            console.log("Failed because: " + error);
          },
          onProgress: function (bytesUploaded, bytesTotal) {
            const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2)
            progressBar.style.width = `${percentage}%`
            textProgress.textContent = `Uploaded ${formatBytes(bytesUploaded)} of ${formatBytes(bytesTotal)} (${percentage})`
            console.log(bytesUploaded, bytesTotal, percentage + "%");
          },
          onSuccess: function () {
            const anchor = document.createElement('a')
            anchor.textContent = `Download ${upload.file.name} (${upload.file.size} bytes)`
            anchor.href = upload.url
            anchor.className = 'btn btn-success'
            uploadList.appendChild(anchor)
            console.log("Download %s from %s", upload.file.name, upload.url);

            reset()
          }
        });

        toggleBtn.addEventListener('click', (e) => {
          upload.start()
        });

        dataDiv.innerHTML +=
          `
          <div class="file-data" key=${i.file.id}>
            <img alt="what" src=${i.id} width="150" />
            <div>name: ${i.file.name}</div>
            <div>type: ${i.file.type}</div>
            <div>size: ${i.file.size}</div>
          </div>
       `
      });
    });
 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tus-js-client@3.0.0-0/dist/tus.min.js"></script>
<div class="upload-card">
      <div class="row">
        <div class="col-md-12">

          <div id="input-section">
            <div class="heading">Select a file you want to upload:</div>
            <input type="file" id="js-file-input" accept="image/*, video/*" multiple>
            <!-- <input type="file" id="input-file" accept="image/*" multiple> -->

          </div>

          <div id="progress-section">
            <div class="heading" id="heading"></div>
            <div class="d-flex">
              <div class="flex-grow-1">
                <div class="progress">
                  <div class="progress-bar progress-bar-striped bg-success" role="progressbar"></div>
                </div>
                <div class="upload-text-progress" id="js-upload-text-progress"></div>
              </div>
              <div class="js-action-btn">
                <button class="btn stop" id="toggle-btn">start</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <hr>
    <h3>Uploads</h3>
    <p>Succesful uploads will be listed here. Try one!</p>
    <p id="upload-list"></p>
    <br>
    <hr>
    <div id="data-pre"></div>
afi
  • 573
  • 4
  • 18

1 Answers1

1

I've found a solution.

It's works for me, you need only change your Javascript code and remove progress bar form HTML. Then you can edit css to make it better. ;)

Edit I've added a demo.

  const fileInput = document.querySelector('#js-file-input')
  const dataDiv = document.querySelector('#data-pre');
  const toggleBtn = document.querySelector('#toggle-btn')
  const uploadList = document.querySelector('#upload-list')


  function reset() {
    convertedFiles.map((i, key) => {
      $('.progress-bar_' + key).css('width', '0%');
      $('#js-upload-text-progress_' + key).html('');
    })
  }
  /**
  * Turn a byte number into a human readable format.
  * Taken from https://stackoverflow.com/a/18650828
  */

  function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 Bytes'

    const k = 1024
    const dm = decimals < 0 ? 0 : decimals
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
    const i = Math.floor(Math.log(bytes) / Math.log(k))
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
  }

  let convertedFiles = [];
  fileInput.addEventListener('change', (e) => {
    dataDiv.innerHTML = '';
    const files = e.target.files;
    Object.keys(files).forEach(k => {
      convertedFiles = [
      ...convertedFiles,
      { id: URL.createObjectURL(files[k]), file: files[k]}
      ];
    });


    convertedFiles.map((i, key) => {
      const file = i.file;
      key ++
      const chunkSize = parseInt(5242880, 10)

      dataDiv.innerHTML +=
      `
      <div class="file-data" key=${key}>
        <img alt="what" src=${i.id} width="150" />
        <div>name: ${i.file.name}</div>
        <div>type: ${i.file.type}</div>
        <div>size: ${i.file.size}</div>
        <div class="flex-grow-1">
          <div class="progress pr_${key}">
            <div class="progress-bar_${key} progress-bar-striped bg-success" role="progressbar"></div>
          </div>
          <div class="upload-text-progress" id="js-upload-text-progress_${key}"></div>
        </div>
      </div>
      `

      let upload = new tus.Upload(file, {
        endpoint   : 'https://tusd.tusdemo.net/files/',
        // endpoint: "http://localhost:8000/tus/upload/",
        chunkSize,
        retryDelays: [0, 1000, 3000, 5000],
        metadata: {
          filename: file.name,
          filetype: file.type
        },
        onError: function (error) {
          console.log("Failed because: " + error);
        },
        onProgress: function (bytesUploaded, bytesTotal) {
          const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2)
          $('.progress-bar_' + key).css('width', percentage + '%');
          $('#js-upload-text-progress_' + key).html(`Uploaded ${formatBytes(bytesUploaded)} of ${formatBytes(bytesTotal)} (${percentage})`);
        },
        onSuccess: function () {
          const anchor = document.createElement('a')
          anchor.textContent = `Download ${upload.file.name} (${upload.file.size} bytes)`
          anchor.href = upload.url
          anchor.className = 'btn btn-success'
          uploadList.appendChild(anchor)
          console.log("Download %s from %s", upload.file.name, upload.url);
        }
      });

      toggleBtn.addEventListener('click', (e) => {
        reset();
        upload.start()
      });

    });
  });
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tus-js-client@3.0.0-0/dist/tus.min.js"></script>
<div class="upload-card">
  <div class="row">
    <div class="col-md-12">

      <div id="input-section">
        <div class="heading">Select a file you want to upload:</div>
        <input type="file" id="js-file-input" accept="image/*, video/*" multiple>
        <!-- <input type="file" id="input-file" accept="image/*" multiple> -->

      </div>

      <div id="progress-section">
        <div class="heading" id="heading"></div>
        <div class="d-flex">
          <div class="js-action-btn">
            <button class="btn stop" id="toggle-btn">start</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
<hr>
<h3>Uploads</h3>
<p>Succesful uploads will be listed here. Try one!</p>
<p id="upload-list"></p>
<br>
<hr>
<div id="data-pre"></div>

Hope it helps

  • Please comment your code and write some explanations so that OP and future readers can see what you have changed, where and why – Can O' Spam Aug 22 '22 at 15:14
  • Yes sorry i'm new here... I've added reset function, because before I've lost them. But differently by example I moved on upload function, so progress bar will be clear everytime. – Tommaso Tonioni Aug 22 '22 at 15:28