Файловое поле обычно выглядит как кнопка с надписью вроде «Выберите файл», с информацией про выбранный файл рядом с ней.
<input type="file">
<script>
var input = document.querySelector("input");
input.addEventListener("change", function() {
if (input.files.length > 0) {
var file = input.files[0];
console.log("You chose", file.name);
if (file.type)
console.log("It has type", file.type);
}
});
</script>
Свойство files
элемента – массивоподобный объект (не настоящий массив), содержащий список выбранных файлов. Изначально он пуст. У элемента нет простого свойства file
, потому что пользователь может выбрать несколько файлов за раз при включённом атрибуте multiple
.
У объектов в свойстве files
есть свойства имя (имя файла), размер (размер файла в байтах), и тип (тип файла в смысле media type — text/plain
или image/jpeg
).
Чего у него нет, так это свойства, содержащего содержимое файла. Чтобы получить содержимое, приходиться постараться. Так как чтение файла с диска занимает длительное время, интерфейс должен быть асинхронным, чтобы документ не замирал. Конструктор FileReader
можно представлять себе, как конструктор XMLHttpRequest
, только для файлов.
<input type="file" multiple>
<script>
var input = document.querySelector("input");
input.addEventListener("change", function() {
Array.prototype.forEach.call(input.files, function(file) {
var reader = new FileReader();
reader.addEventListener("load", function() {
console.log("File", file.name, "starts with",
reader.result.slice(0, 20));
});
reader.readAsText(file);
});
});
</script>
Чтение файла происходит при помощи создания объекта FileReader
, регистрации события “load”
для него, и вызова его метода readAsText
с передачей тому файла. По окончанию загрузки в свойстве result
сохраняется содержимое файла.
Пример использует Array.prototype.forEach
для прохода по массиву, так как в обычном цикле было бы неудобно получать нужные объекты file
и reader
от обработчика событий. Переменные были бы общими для всех итераций цикла.
У FileReaders
также есть событие “error”
, когда чтение файла не получается. Объект error
будет сохранён в свойстве error
. Если вы не хотите забивать голову ещё одной неудобной асинхронной схемой, вы можете обернуть её в обещание (см. главу 17):
function readFile(file) {
return new Promise(function(succeed, fail) {
var reader = new FileReader();
reader.addEventListener("load", function() {
succeed(reader.result);
});
reader.addEventListener("error", function() {
fail(reader.error);
});
reader.readAsText(file);
});
}
Возможно читать только часть файла, вызывая slice
и передавая результат (т. н. объект blob
) объекту reader
.
Хранение данных на стороне клиента
Простые HTML-странички с добавкой JavaScript могут выступать отличной основой для мини-приложений – небольших вспомогательных программ, автоматизирующих ежедневные дела. Присоединив к полям формы обработчики событий вы можете делать всё – от конвертации фаренгейтов в цельсии до генерации паролей из основного пароля и имени веб-сайта.
Когда такому приложению нужно сохранять информацию между сессиями, переменные JavaScript использовать не получится – их значения выбрасываются каждый раз при закрытии страницы. Можно было бы настроить сервер, подсоединить его к интернету и тогда приложение хранило бы ваши данные там. Это мы разберём в главе 20. Но это добавляет вам работы и сложности. Иногда достаточно хранить данные в своём браузере. Но как?
Можно хранить строковые данные так, что они переживут перезагрузку страниц — для этого надо положить их в объект localStorage
. Он разрешает хранить строковые данные под именами (которые тоже являются строками), как в этом примере:
localStorage.setItem("username", "marijn");
console.log(localStorage.getItem("username"));
// → marijn
localStorage.removeItem("username");
Переменная в localStorage
хранится, пока её не перезапишут, удаляется при помощи removeItem
или очисткой локального хранилища пользователем.
У сайтов с разных доменов – разные отделения в этом хранилище. То есть, данные, сохранённые с вебсайта в localStorage
, могут быть прочтены или перезаписаны только скриптами с этого же сайта.