Đọc tệp trong JavaScript

Việc chọn và tương tác với các tệp trên thiết bị cục bộ của người dùng là một trong những việc các tính năng thường dùng nhất của web. Chế độ này cho phép người dùng chọn tệp và hãy tải chúng lên máy chủ, ví dụ: khi chia sẻ ảnh hoặc gửi thông tin thuế tài liệu. AI của Google cũng cho phép các trang web đọc và thao tác chúng mà không cần để chuyển dữ liệu qua mạng. Trang này hướng dẫn cách sử dụng JavaScript để tương tác với các tệp.

API Truy cập hệ thống tệp hiện đại

API Truy cập hệ thống tệp cung cấp cách thức để đọc và ghi vào tệp và trên hệ thống cục bộ của người dùng. Tính năng này hoạt động trong hầu hết các phiên bản dựa trên Chromium các trình duyệt như Chrome và Edge. Để tìm hiểu thêm về vấn đề này, hãy tham khảo API Truy cập hệ thống tệp.

Do API Truy cập hệ thống tệp không tương thích với tất cả các trình duyệt, nên chúng tôi khuyên bạn nên sử dụng browser-fs-access một thư viện trợ giúp sử dụng API mới bất cứ khi nào có sẵn và phù hợp quay lại phương pháp tiếp cận cũ nếu thực tế không phải vậy.

Làm việc với tệp theo cách cổ điển

Hướng dẫn này cho bạn biết cách tương tác với các tệp bằng các phương thức JavaScript cũ.

Chọn tệp

Có hai cách chính để chọn tệp: sử dụng phần tử đầu vào HTML và sử dụng vùng kéo và thả.

Phần tử nhập HTML

Cách dễ nhất để người dùng chọn tệp là sử dụng <input type="file"> được hỗ trợ trong mọi trình duyệt chính. Khi nhấp vào, người dùng chọn một hoặc nhiều tệp nếu multiple được bao gồm, bằng cách sử dụng lựa chọn tệp tích hợp sẵn của hệ điều hành Giao diện người dùng. Khi người dùng chọn xong một hoặc nhiều tệp, change của phần tử kích hoạt sự kiện. Bạn có thể truy cập vào danh sách các tệp trong event.target.files là đối tượng FileList. Mỗi mục trong FileList là một đối tượng File.

<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
  const fileSelector = document.getElementById('file-selector');
  fileSelector.addEventListener('change', (event) => {
    const fileList = event.target.files;
    console.log(fileList);
  });
</script>

Ví dụ sau đây cho phép người dùng chọn nhiều tệp bằng giao diện người dùng chọn tệp tích hợp sẵn của hệ thống, sau đó ghi nhật ký từng tệp đã chọn vào Google Play.

Giới hạn các loại tệp mà người dùng có thể chọn

Trong một số trường hợp, bạn có thể muốn giới hạn các loại tệp mà người dùng có thể chọn. Cho ví dụ: ứng dụng chỉnh sửa hình ảnh chỉ nên chấp nhận hình ảnh chứ không chấp nhận tệp văn bản. Để đặt hãy thêm các quy tắc hạn chế về loại tệp accept cho phần tử đầu vào để chỉ định loại tệp được chấp nhận:

<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">

Kéo và thả tuỳ chỉnh

Trong một số trình duyệt, phần tử <input type="file"> cũng là mục tiêu thả. cho phép người dùng kéo và thả tệp vào ứng dụng của bạn. Tuy nhiên, mục tiêu giảm này nhỏ và có thể khó sử dụng. Thay vào đó, sau khi bạn cung cấp các tính năng cốt lõi bằng phần tử <input type="file">, bạn có thể cung cấp thao tác kéo và thả lớn, tuỳ chỉnh nền tảng.

Chọn vùng thả của bạn

Nền tảng thả phụ thuộc vào thiết kế của ứng dụng. Có thể bạn chỉ muốn của cửa sổ làm giao diện thả, nhưng bạn có thể sử dụng toàn bộ cửa sổ.

Ảnh chụp màn hình của Squoosh, một ứng dụng web nén ảnh.
Squoosh đặt toàn bộ cửa sổ thành một vùng thả.

Ứng dụng nén hình ảnh Squoosh cho phép người dùng kéo hình ảnh vào bất cứ đâu vào cửa sổ và nhấp vào chọn một hình ảnh để gọi <input type="file"> . Bất kể bạn chọn vùng hiển thị như thế nào, hãy đảm bảo người dùng hiểu rõ nội dung để họ có thể kéo tệp lên bề mặt đó.

Xác định vùng thả

Để bật một phần tử làm vùng kéo và thả, hãy tạo trình nghe cho hai sự kiện: dragoverdrop. Sự kiện dragover sẽ cập nhật giao diện người dùng của trình duyệt để cho thấy rằng thao tác kéo và thả là tạo bản sao của tệp. Sự kiện drop sẽ kích hoạt sau khi người dùng thả tệp xuống nền tảng. Giống như phần tử đầu vào, bạn có thể truy cập vào danh sách các tệp từ event.dataTransfer.files, đây là Đối tượng FileList. Một mục trong FileList là đối tượng File.

const dropArea = document.getElementById('drop-area');

dropArea.addEventListener('dragover', (event) => {
  event.stopPropagation();
  event.preventDefault();
  // Style the drag-and-drop as a "copy file" operation.
  event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
  event.stopPropagation();
  event.preventDefault();
  const fileList = event.dataTransfer.files;
  console.log(fileList);
});

event.stopPropagation()event.preventDefault() dừng hành vi mặc định của trình duyệt và để mã của bạn chạy thay thế. Nếu không có chúng, trình duyệt sẽ điều hướng khỏi trang của bạn và mở các tệp người dùng thả vào cửa sổ trình duyệt.

Để xem bản minh hoạ trực tiếp, hãy tham khảo bài viết Kéo và thả tuỳ chỉnh.

Thư mục thì sao?

Rất tiếc, không có cách hay để truy cập vào thư mục bằng JavaScript.

webkitdirectory trên phần tử <input type="file"> cho phép người dùng chọn một thư mục hoặc thư mục. Trình duyệt này được hỗ trợ trong hầu hết các trình duyệt chính ngoại trừ Firefox cho Android và Safari trên iOS.

Nếu bạn bật tính năng kéo và thả, người dùng có thể cố gắng kéo một thư mục vào vùng thả. Khi sự kiện thả kích hoạt, sự kiện này sẽ bao gồm một đối tượng File cho giá trị nhưng không cấp quyền truy cập vào bất kỳ tệp nào trong thư mục đó.

Đọc siêu dữ liệu tệp

Đối tượng File chứa siêu dữ liệu về tệp đó. Hầu hết trình duyệt cung cấp tên tệp, kích thước của tệp và loại MIME, mặc dù tuỳ thuộc vào trên nền tảng, các trình duyệt khác nhau có thể cung cấp các của bạn.

function getMetadataForFileList(fileList) {
  for (const file of fileList) {
    // Not supported in Safari for iOS.
    const name = file.name ? file.name : 'NOT SUPPORTED';
    // Not supported in Firefox for Android or Opera for Android.
    const type = file.type ? file.type : 'NOT SUPPORTED';
    // Unknown cross-browser support.
    const size = file.size ? file.size : 'NOT SUPPORTED';
    console.log({file, name, type, size});
  }
}

Bạn có thể xem ví dụ thực tế trong input-type-file bản minh hoạ.

Đọc nội dung của tệp

Sử dụng FileReader để đọc nội dung của đối tượng File vào bộ nhớ. Bạn có thể yêu cầu FileReader đọc tệp dưới dạng vùng đệm mảng, một URL dữ liệu, hoặc văn bản:

function readImage(file) {
  // Check if the file is an image.
  if (file.type && !file.type.startsWith('image/')) {
    console.log('File is not an image.', file.type, file);
    return;
  }

  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    img.src = event.target.result;
  });
  reader.readAsDataURL(file);
}

Ví dụ này đọc một File do người dùng cung cấp, sau đó chuyển đổi nó thành một dữ liệu URL rồi sử dụng URL dữ liệu đó để hiển thị hình ảnh trong phần tử img. Để tìm hiểu cách xác minh rằng người dùng đã chọn một tệp hình ảnh, hãy tham khảo Bản minh hoạ read-image-file.

Theo dõi tiến trình đọc tệp

Khi đọc các tệp lớn, việc cung cấp một số trải nghiệm người dùng để cho người dùng biết có thể rất hữu ích tiến trình đọc đến đâu. Để làm được điều đó, hãy sử dụng progress sự kiện do FileReader cung cấp. Sự kiện progress có 2 thuộc tính: loaded (lượng cần đọc) và total (lượng cần đọc).

function readFile(file) {
  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    const result = event.target.result;
    // Do something with result
  });

  reader.addEventListener('progress', (event) => {
    if (event.loaded && event.total) {
      const percent = (event.loaded / event.total) * 100;
      console.log(`Progress: ${Math.round(percent)}`);
    }
  });
  reader.readAsDataURL(file);
}

Hình ảnh chính của Vincent B lui trên Unsplash