+1

Control Upload File

Trong bài viết này, mình sẽ sử dụng ví dụ về upload file ảnh để nói về các vấn đề cơ bản về upload file trong java script mà mình tìm hiểu được.

1. Upload single file:

  • Đề upload file trong javascript, ta sử dụng class FileReader và sự kiện onload()
  • Ta tạo file upload_single_file.html và upload_single_file.js để thực hiện upload và hiển thị 1 file ảnh duy nhất.
  • Ta thêm sẵn các thư viện bootstrapjQuery để tiện sử dụng.
<!DOCTYPE html>
<html>
<head>
  <title>UPLOAD SINGLE FILE</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 
  <script src="scripts/upload_single_file.js"></script>
</head>
<body>
  <div class="container">
    <h2>Upload single file</h2>
    <form>
      <div class="form-group">
        <label>Select single image file</label>
        <input type="file" class="form-control" id="fileInput">
      </div>
    </form>
  </div>
  <div class="container js-file-list">
  </div>  
</body>
</html>
$(document).ready(function(){
  $('#fileInput').on('change', function() {
    var file = $(this)[0].files[0];

    var fileReader = new FileReader();
    fileReader.onload = function() {
      var str = '<div class="col-md-2">' +
        '<span class="js-file-name"></span><br>' +
        '<span class="js-file-size"></span> (Byte)<br>' +
        '<img class="img-thumbnail js-file-image" style="width: 100%; height: 100%">' +
      '</div>';
      $('.js-file-list').append(str);

      var imageSrc = event.target.result;
      var fileName = file.name;
      var fileSize = file.size;
      $('.js-file-name').text(fileName);
      $('.js-file-size').text(fileSize);
      $('.js-file-image').attr('src', imageSrc);
    };
    fileReader.readAsDataURL(file);
  });
});
  • Kết quả thu được

2. Upload multi file:

  • Để upload multi file, ta thêm thuộc tính multiple vào file input.
  • Ta có file upload_multi_file.html như sau, không khác nhiều so với upload_single_file.html.
<!DOCTYPE html>
<html>
<head>
  <title>UPLOAD MULTI FILE</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 
  <script src="scripts/upload_multi_file.js"></script>
</head>
<body>
  <div class="container">
    <h2>Upload multi file</h2>
    <form>
      <div class="form-group">
        <label>Select multi image files</label>
        <input type="file" class="form-control" id="fileInput" multiple>
      </div>
    </form>
  </div>
  <div class="container js-file-list">
  </div>  
</body>
</html>
  • Ta tạo file upload_multi_file.js để xử lí vệc upload file
$(document).ready(function(){
  $('#fileInput').on('change', function() {
    var files = $(this)[0].files;

    for(var i = 0; i < files.length; i++) {
      var file = files[i];
      var fileReader = new FileReader();
      fileReader.onload = function() {
        var str = '<div class="col-md-2">' +
          '<span class="js-file-name"></span><br>' +
          '<span class="js-file-size"></span> (Byte)<br>' +
          '<img class="img-thumbnail js-file-image" style="width: 100%; height: 100%">' +
          '</div>';
        $('.js-file-list').append(str);

        var imageSrc = event.target.result;
        var fileName = file.name;
        var fileSize = file.size;
        $('.js-file-name').last().text(fileName);
        $('.js-file-size').last().text(fileSize);
        $('.js-file-image').last().attr('src', imageSrc);
      };
      fileReader.readAsDataURL(file);
    }  
  });
});
  • Ta tạo vòng lặp và xử lý tương tự như việc upload 1 file đối với từng file trong vòng lặp.

  • Kết quả thu được

  • Ta thấy file hiển thị đúng hình ảnh nhưng thông tin file đang bị sai (chỉ hiển thị duy nhất thông tin của 1 file).

  • Ta thực hiện thêm console.log() để DEBUG.

$(document).ready(function(){
  $('#fileInput').on('change', function() {
    var files = $(this)[0].files;

    for(var i = 0; i < files.length; i++) {
      var file = files[i];

      console.log('DEBUG FOR');
      console.log(i, file.name, file.size);

      var fileReader = new FileReader();
      fileReader.onload = function() {
        console.log('DEBUG ONLOAD');
        console.log(file.name, file.size);

        var str = '<div class="col-md-2">' +
          '<span class="js-file-name"></span><br>' +
          '<span class="js-file-size"></span> (Byte)<br>' +
          '<img class="img-thumbnail js-file-image" style="width: 100%; height: 100%">' +
          '</div>';
        $('.js-file-list').append(str);

        var imageSrc = event.target.result;
        var fileName = file.name;
        var fileSize = file.size;
        $('.js-file-name').last().text(fileName);
        $('.js-file-size').last().text(fileSize);
        $('.js-file-image').last().attr('src', imageSrc);
      };
      fileReader.readAsDataURL(file);
    }  
  });
});
  • Kết quả thu được:

  • Ta thấy hàm onload() chạy sau khi vòng lặp for kết thúc do đó biến file nhận được trong hàm onload() là file cuối cùng được upload.
  • Để giải quyết vấn đề này, ta thực hiện truyền biến file vào trong hàm onload()
  • Cú pháp truyền biến file vào hàm onload() như sau:
reader.onload = (function(fileParams) {
  return function() {
     // use fileParams here ...
  };
})(fileParam);
  • Ta update nội dung file upload_multi_file.js như sau:
$(document).ready(function(){
  $('#fileInput').on('change', function() {
    var files = $(this)[0].files;

    for(var i = 0; i < files.length; i++) {
      var file = files[i];
      var fileReader = new FileReader();
      fileReader.onload = (function(fileParams) {
        return function(event) {
          var str = '<div class="col-md-2">' +
            '<span class="js-file-name"></span><br>' +
            '<span class="js-file-size"></span> (Byte)<br>' +
            '<img class="img-thumbnail js-file-image" style="width: 100%; height: 100%">' +
          '</div>';
          $('.js-file-list').append(str);

          var imageSrc = event.target.result;
          var fileName = fileParams.name;
          var fileSize = fileParams.size;

          $('.js-file-name').last().text(fileName);
          $('.js-file-size').last().text(fileSize);
          $('.js-file-image').last().attr('src', imageSrc);
        };
      })(file);  
      fileReader.readAsDataURL(file);
    }  
  });
});
  • Kết quả thu được:
  • Ta thấy thông tin file đã hiển thị đúng thông tin nhưng không đúng theo thứ tự upload file, vấn đề này sẽ được giải quyết trong ví dụ tiếp theo.

3. Upload multi file with correct order:

  • Trong ví dụ trên, ta thực hiện việc upload và show file trong vòng lặp, các FileReader object được tạo tuần tự nhưng hàmonload() chỉ chạy khi việc upload file hoàn thành.
  • Do đó các hàm onload() không được chạy tuần tự như việc tạo FileReader object và gây ra việc các file không được upload đúng theo thứ tự mong muốn.
  • Để giải quyết vấn đề này, ta cần đảm bảo các hàm onload() được chạy tuần tự đúng theo thứ tự upload file.
  • Trong ví dụ này, mình sử dụng phương pháp đệ quy, sau khi FileRaeder object được tạo hàm onload() của 1 file chạy xong thì tiếp tục tạo FileReader object và hàm onload() cho file tiếp theo, với cách làm này, ta không cần thực hiện truyền biến file vào hàm onload().
  • Tạo function đệ quy uploadFile(files, index) như sau
function uploadFile(files, index) {
    var length = files.length
    if (index == length) {
      return;
    }

    var file = files[index];
    var fileReader = new FileReader();
    fileReader.onload = function() {
      var str = '<div class="col-md-2">' +
        '<span class="js-file-name"></span><br>' +
        '<span class="js-file-size"></span> (Byte)<br>' +
        '<img class="img-thumbnail js-file-image" style="width: 100%; height: 100%">' +
        '</div>';
      $('.js-file-list').append(str);

      var imageSrc = event.target.result;
      var fileName = file.name;
      var fileSize = file.size;

      $('.js-file-name').last().text(fileName);
      $('.js-file-size').last().text(fileSize);
      $('.js-file-image').last().attr('src', imageSrc);

      uploadFile(files, index + 1)
    };
    fileReader.readAsDataURL(file);
  }
  • Thực hiện gọi hàm uploadFile()
  $('#fileInput').on('change', function() {
    var files = $(this)[0].files;
    uploadFile(files, 0)
  });
  • Kết quả thu được:

4. Source code tham khảo:


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí