Событие выбора файла в input type file
Using the File API, which was added to the DOM in HTML5, it's now possible for web content to ask the user to select local files and then read the contents of those files. This selection can be done by either using an HTML element or by drag and drop.
If you want to use the DOM File API from extensions or other browser chrome code, you can; however, note there are some additional features to be aware of. See Using the DOM File API in chrome code for details.
Пример: Отображение эскизов изображений, выбранных пользователем
Представим, что вы разрабатываете очередной веб-сайт для обмена фотографиями и вы хотите использовать возможности HTML5 для предварительного просмотра изображений перед тем, как пользователь загрузит их. Вы можете создать input элемент или зону перетаскивания, как обсуждалось ранее, и вызвать такую функцию, как handleFiles() ниже.
Здесь наш цикл обрабатывает выбранные пользователем файлы, проверяя атрибут type у каждого файла, чтобы определить является ли файл изображением (выполняется регулярное выражение в строке " image.* "). Для каждого файла, который является изображением, мы создаём новый img элемент. Можно использовать CSS для установки красивых рамок, теней, и указания размеров изображения, но здесь нет нужды делать этого.
Каждое изображение имеет CSS класс obj добавленный к нему для упрощения его поиска в DOM дереве. Мы также добавили атрибут file к каждому изображению, указав File ; это позволит нам получить изображения для фактической загрузки позже. Наконец, мы используем Node.appendChild() для того, чтобы добавить новый эскиз в область предпросмотра нашего документа.
Затем мы устанавливаем FileReader для обработки асинхронной загрузки изображения и прикрепления его к img элементу. После создания нового объекта FileReader , мы настраиваем его функцию onload , затем вызываем readAsDataURL() для запуска операции чтения в фоновом режиме. Когда всё содержимое файла изображения загружено, они преобразуют его в data: URL, который передаётся в колбэк onload . Наша реализация этой процедуры просто устанавливает атрибут src у элемента img загруженного изображения, в результате чего миниатюра изображения появляется на экране пользователя.
Accessing selected file(s) on a change event
It is also possible (but not mandatory) to access the FileList through the change event. You need to use EventTarget.addEventListener() to add the change event listener, like this:
Example: Showing file(s) size
The following example shows a possible use of the size property:
Using object URLs
The DOM URL.createObjectURL() and URL.revokeObjectURL() methods let you create simple URL strings that can be used to reference any data that can be referred to using a DOM File object, including local files on the user's computer.
When you have a File object you'd like to reference by URL from HTML, you can create an object URL for it like this:
The object URL is a string identifying the File object. Each time you call URL.createObjectURL() , a unique object URL is created even if you've created an object URL for that file already. Each of these must be released. While they are released automatically when the document is unloaded, if your page uses them dynamically you should release them explicitly by calling URL.revokeObjectURL() :
Использование URLs объектов
Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) представляет поддержку для методов DOM window.URL.createObjectURL() (en-US) и window.URL.revokeObjectURL() (en-US). Они позволяют создавать простые строки URL, которые могут быть использованы для обращения к любым данным, на которые можно ссылаться, используя объект DOM File , включая локальные файлы на компьютере пользователя.
Когда у вас есть объект File , на который вы хотите ссылаться по URL из HTML, вы можете создать для этого объект URL, такой как этот:
URL объекта – это строка, идентифицирующая объект файла File . Каждый раз при вызове window.URL.createObjectURL() (en-US), создаётся новый уникальный объект URL, даже если вы уже создали объект URL для этого файла. Каждый из них должен быть освобождён. В то время как они освобождаются автоматически когда документ выгружается, если ваша страница использует их динамически, вы должны освободить их явно вызовом window.URL.revokeObjectURL() (en-US):
Asynchronously handling the file upload process
This example, which uses PHP on the server side and JavaScript on the client side, demonstrates asynchronous uploading of a file.
Accessing selected file(s)
Consider this HTML:
The File API makes it possible to access a FileList containing File objects representing the files selected by the user.
The multiple attribute on the input element allows the user to select multiple files.
Accessing the first selected file using a classical DOM selector:
Доступ к выбранным файлам через событие change
Также возможно (но не обязательно) получить доступ к FileList через событие change . Нужно использовать EventTarget.addEventListener() чтобы добавить обработчик события change , как показано здесь:
Обработчик события change можно назначить атрибутом элемента:
Когда пользователь выбирает файл, функция handleFiles() будет вызвана с объектом FileList , который состоит из объектов File , представляющих файлы, выбранные пользователем.
Использование элемента label скрытого элемента input
Для того, чтобы открыть диалог выбора файла без использования JavaScript (метода click()), можно воспользоваться элементом .
Рассмотрим следующую разметку HTML:
В данном случае нет необходимости добавлять код JavaScript для того, чтобы вызвать fileElem.click() . Также в данном случае вы можете стилизовать элемент label так, как пожелаете.
Getting information about selected file(s)
The FileList object provided by the DOM lists all of the files selected by the user, each specified as a File object. You can determine how many files the user selected by checking the value of the file list's length attribute:
Individual File objects can be retrieved by accessing the list as an array:
This loop iterates over all the files in the file list.
There are three attributes provided by the File object that contain useful information about the file.
The file's name as a read-only string. This is just the file name, and does not include any path information.
The size of the file in bytes as a read-only 64-bit integer.
The MIME type of the file as a read-only string or "" if the type couldn't be determined.
Получение информации о выделенных файлах
Объект FileList предоставляемый классическим DOM содержит все файлы выбранные пользователем, каждый из которых представляет собой объект File . Вы можете определить сколько файлов выбрал пользователь проверяя значение атрибута длины ( length ) списка файлов:
Конкретные объекты File могут быть получены обращением к списку файлов как к массиву:
Этот цикл проходит по всем файлам в списке файлов.
Всего существует три атрибута, предоставляемых объектом File , которые содержат полезную информацию о файле.
name Имя файла как строка доступная только для чтения. Это просто имя файла и оно не включает в себя информацию о пути. size Размер файла в байтах, как 64-битное целое число (возможно только чтение). type MIME тип файла, как строка доступная только для чтения, или пустая строка ( "") если тип файла не может быть определён.
Selecting files using drag and drop
You can also let the user drag and drop files into your web application.
The first step is to establish a drop zone. Exactly what part of your content will accept drops may vary depending on the design of your application, but making an element receive drop events is easy:
In this example, we're turning the element with the ID dropbox into our drop zone. This is done by adding listeners for the dragenter , dragover , and drop events.
We don't actually need to do anything with the dragenter and dragover events in our case, so these functions are both simple. They just stop propagation of the event and prevent the default action from occurring:
The real magic happens in the drop() function:
Here, we retrieve the dataTransfer field from the event, pull the file list out of it, and then pass that to handleFiles() . From this point on, handling the files is the same whether the user used the input element or drag and drop.
Using hidden file input elements using the click() method
Consider this HTML:
The code that handles the click event can look like this:
You can style the new button for opening the file picker as you wish.
Управление процессом загрузки файла
Функция FileUpload принимает на вход 2 параметра: элемент изображения и файл, из которого нужно читать данные изображения.
Перед началом загрузки данных выполняются несколько шагов для подготовки:
Using a label element to trigger a hidden file input element
Consider this HTML:
There is no need to add JavaScript code to call fileElem.click() . Also in this case you can style the label element as you wish. You need to provide a visual cue for the focus status of the hidden input field on its label, be it an outline as shown above, or background-color or box-shadow. (As of time of writing, Firefox doesn't show this visual cue for elements.)
Пример: Загрузка файла, выбранного пользователем
Ещё одна вещь, которую вы можете захотеть сделать – это позволить пользователю загрузить выбранный файл или файлы (такие, как изображения из предыдущего примера) на сервер. Это можно сделать асинхронно довольно просто.
Example: Using object URLs to display PDF
Object URLs can be used for other things than just images! They can be used to display embedded PDF files or any other resources that can be displayed by the browser.
In Firefox, to have the PDF appear embedded in the iframe (rather than proposed as a downloaded file), the preference pdfjs.disabled must be set to false Non-Standard .
And here is the change of the src attribute:
Handling the upload process for a file
The FileUpload function accepts two inputs: an image element and a file from which to read the image data.
Before actually transferring the data, several preparatory steps are taken:
Использование метода click() скрытых элементов выбора файла
Рассмотрим следующую разметку HTML:
Код, обрабатывающий событие click, может выглядеть следующим образом:
Таким образом, вы можете стилизовать новую кнопку открытия диалога выбора файла так, как пожелаете.
Создание заданий на загрузку
Продолжая пример с кодом, который строил эскизы в предыдущем примере, напомним, что каждому изображению эскиза присвоен класс CSS class obj , с соответствующим File , прикреплённым в атрибут file . Это позволяет нам очень просто выбрать все изображения, которые пользователь выбрал для загрузки используя Document.querySelectorAll() , как показано здесь:
Строка 2 получает NodeList в переменную imgs со всеми элементами документа, имеющих класс CSS obj . В нашем случае все они будут эскизами изображений. Как только мы получим этот список, можно просто пройти по нему, создавая для каждого элемента новый экземпляр FileUpload . Каждый из них отвечает за загрузку соответствующего файла.
Example: Showing thumbnails of user-selected images
Let's say you're developing the next great photo-sharing website and want to use HTML to display thumbnail previews of images before the user actually uploads them. You can establish your input element or drop zone as discussed previously and have them call a function such as the handleFiles() function below.
Here our loop handling the user-selected files looks at each file's type attribute to see if its MIME type begins with the string " image/ "). For each file that is an image, we create a new img element. CSS can be used to establish any pretty borders or shadows and to specify the size of the image, so that doesn't need to be done here.
Each image has the CSS class obj added to it, making it easy to find in the DOM tree. We also add a file attribute to each image specifying the File for the image; this will let us fetch the images for actual upload later. We use Node.appendChild() to add the new thumbnail to the preview area of our document.
Next, we establish the FileReader to handle asynchronously loading the image and attaching it to the img element. After creating the new FileReader object, we set up its onload function and then call readAsDataURL() to start the read operation in the background. When the entire contents of the image file are loaded, they are converted into a data: URL which is passed to the onload callback. Our implementation of this routine sets the img element's src attribute to the loaded image which results in the image appearing in the thumbnail on the user's screen.
Выбор файлов с использованием технологии drag and drop
Также вы можете предоставить пользователю возможность непосредственно перетаскивать файлы в ваше веб-приложение.
На первом шаге необходимо определить зону, в которую будут перетаскиваться файлы. В каждом конкретном случае часть содержимого вашей страницы, ответственная за приёмку перетаскиваемых файлов, может варьироваться в зависимости от дизайна приложения, тем не менее, заставить элемент воспринимать события перетаскивания достаточно просто:
В данном примере мы превращаем элемент с ID, равным dropbox, в нашу зону перетаскивания при помощи добавления обработчиков для событий dragenter , dragover и drop .
В нашем случае нет необходимости делать что-то особенное при обработке событий dragenter и dragover , таким образом, обе функции, ответственные за обработку данных событий, довольно просты. Они останавливают распространение события и предотвращают возникновение действия по умолчанию:
Вся настоящая магия происходит в функции drop() :
Здесь мы извлекаем из события поле dataTransfer , затем вытаскиваем из него список файлов и передаём этот список в handleFiles() . После этого процесс обработки файлов одинаков вне зависимости от того, использовал ли пользователь для их выбора элемент input или технологию drag and drop.
Пример: Использование URL объектов для отображения изображений
Этот пример использует URL объектов для отображения эскизов изображений. Кроме этого, оно показывает другую информацию о файлах, включая их имена и размеры. Вы можете посмотреть работающий пример.
HTML, который представляет интерфейс, выглядит так:
Метод handleFiles() может быть реализован таким образом:
Если объект FileList , передаваемый в handleFiles() является null , то мы просто устанавливаем внутренний HTML блока в отображение текста "No files selected!". Иначе мы начинаем строить список файлов таким образом:
- Создаётся новый элемент - неупорядоченный список ( ).
- Этот новый элемент вставляется в блок с помощью вызова его метода element.appendChild() .
- Для каждого File в FileList , представляемого files :
- Создаём новый элемент пункта списка ( ) и вставляем его в список.
- Создаём новый элемент изображения ( ).
- Устанавливаем источник изображения в новый URL объекта, представляющий файл, используя window.URL.createObjectURL() (en-US) для создания URL на двоичный объект.
- Устанавливаем высоту изображения в 60 пикселей.
- Устанавливаем обработчик события загрузки изображения для освобождения URL объекта, т.к. после загрузки изображения он больше не нужен. Это делается вызовом метода window.URL.revokeObjectURL() (en-US), передавая в него строку URL объекта, которая указана в img.src .
- Добавляем новый элемент в список.
Асинхронная обработка процесса загрузки
Example: Using object URLs with other file types
You can manipulate files of other formats the same way. Here is how to preview uploaded video:
Is there any chance to detect every file selection the user made for an HTML input of type file element?
This was asked many times before, but the usually proposed onchange event doesn't fire if the user select the same file again.
Would your code then also have to fire if the user hits Cancel then? One expects that hitting Cancel will do nothing, and I think most users would further expect that re-selecting the same file would have the same effect as Cancel. I don't know if this is possible or not but I suggest you reconsider this design anyway.
On cancel it should not fire or make it otherwise detectable. It is more meant to remove an UI ceveat: If some action is invoked after the file is choosen, the user usually expect the action to repeat if he choose the file again.
Maybe we can have this behaviour if we set the input s value to '' after doing something with the file. But that would remove the visible filename too. However, that may be ok, as the file is actually processed and the result of that action may appear somewhere else.
All I want is to simulate the old school behaviour desktop applications have. If I 'open' the same file again in an desktop application, it is usually reloaded, or if some action is done with the file (like converting it so another format for example) this action is done again. This is what desktop users may expect from a web app too, but the file input onchange event doesn't resemble.
Доступ к выбранным файлам
Рассмотрим следующий код:
File API делает возможным доступ к FileList , который содержит объекты File , которым соответствуют файлы, выбранные пользователем.
Атрибут multiple элемента input позволяет пользователю выбрать несколько файлов.
Обращение к одному выбранному файлу с использованием классической DOM-модели:
Обращение к одному выбранному файлу через jQuery:
Ошибка "files is undefined" означает что был выбран не один HTML-элемент, а список элементов, возвращаемый jQuery. Необходимо уточнить, у какого именно элемента требуется вызвать метод "files"
Creating the upload tasks
Continuing with the code that built the thumbnails in the previous example, recall that every thumbnail image is in the CSS class obj with the corresponding File attached in a file attribute. This allows us to select all of the images the user has chosen for uploading using Document.querySelectorAll() , like this:
Line 2 fetches a NodeList , called imgs , of all the elements in the document with the CSS class obj . In our case, these will be all of the image thumbnails. Once we have that list, it's trivial to go through it and create a new FileUpload instance for each. Each of these handles uploading the corresponding file.
Пример: Отображение размера файла(ов)
Следующий пример показывает возможное использование свойства size :
Пример: Использование URL объектов с другими типами файлов
Вы можете таким же образом работать с файлами в других форматах. Ниже приведён пример как загружается видео:
Example: Using object URLs to display images
This example uses object URLs to display image thumbnails. In addition, it displays other file information including their names and sizes.
The HTML that presents the interface looks like this:
The handleFiles() method follows:
If the FileList object passed to handleFiles() is null , we set the inner HTML of the block to display "No files selected!". Otherwise, we start building our file list, as follows:
- A new unordered list ( ) element is created.
- The new list element is inserted into the block by calling its Node.appendChild() method.
- For each File in the FileList represented by files :
- Create a new list item ( ) element and insert it into the list.
- Create a new image ( ) element.
- Set the image's source to a new object URL representing the file, using URL.createObjectURL() to create the blob URL.
- Set the image's height to 60 pixels.
- Set up the image's load event handler to release the object URL since it's no longer needed once the image has been loaded. This is done by calling the URL.revokeObjectURL() method and passing in the object URL string as specified by img.src .
- Append the new list item to the list.
Here is a live demo of the code above:
Пример: Использование URL объектов для отображения PDF
URL объектов могут быть использованы не только для изображений! Также этот приём можно использовать и для других ресурсов, которые могут отображаться браузером, например, файлы PDF.
В Firefox, для того чтобы файл PDF появился в iframe и не предлагался для загрузки, нужно установить pdfjs.disabled в значение false Non-Standard .
А здесь изменение атрибута src :
Example: Uploading a user-selected file
Another thing you might want to do is let the user upload the selected file or files (such as the images selected using the previous example) to a server. This can be done asynchronously very easily.
9 Answers 9
Set the value of the input to null on each onclick event. This will reset the input 's value and trigger the onchange event even if the same path is selected.
Note: It's normal if your file is prefixed with 'C:\fakepath'. That's a security feature preventing JavaScript from knowing the file's absolute path. The browser still knows it internally.
I tried the demo, but (using Chrome 21) I keep getting `C:\fakepath` + then the filename I selected (excluding the path). The alert is show on every select of the same file though.
@JasperdeVries That's a security feature preventing JavaScript from knowing a file's absolute path. The browser still knows the path internally.
As an aside, null is the only permitted value for . So if you're trying to set it to undefined and getting an exception then that's the reason why.
It's better to put this.value = null at the end of the onchange because it's possible to active an input element without clicking on it (by using the keyboard). You can store input.files if you need to reference it later.
Use onClick event to clear value of target input, each time user clicks on field. This ensures that the onChange event will be triggered for the same file as well. Worked for me :)
this.value=null; is only necessary for Chrome, Firefox will work fine just with return false;
simple and effective just tested in latest IE and chrome and works like a charm. Thanks for sharing it
In this article, under the title "Using form input for selecting"
It adds an event listener to 'change', but I tested it and it triggers even if you choose the same file and not if you cancel.
Considering the potentially ambiguous meaning of "change" in this case, did you try this in multiple browsers? You may want to specify which ones you tried it on as on the web, browsers aren't exactly always in consensus about these things.
What environment you use for your test? My experience with Chrome was the one I stated in the question, the change event would fire only on filename changes.
Hey ngCourse ! Code-only answers may solve the problem but they are much more useful if you explain how they solve it. Community requires theory as well as code both to understand your answer fully.
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply. From Review
Загрузка файлов всегда занимала особое место в веб-разработке.
О трудности оформления стилями уже сказано немало, почитать об этом можно, например, по ссылкам раз, два, три, четыре, пять, шесть.
Но и сам процесс загрузки файлов нетривиален, есть много разных способов – и ни одного идеального.Пока писалась статья, Chrome 9 был объявлен stable и форсировано обновился уже на 75% установок 8 версии. Так, что празднуем поддержку File API первым стабильным браузером, ура!
Мы подумали, что не использовать такую технологию было бы преступлением против
юзеровпользователей.
Подумали — и внедрили html5 загрузку в дополнение к уже существующим вариантам.
В итоге наши пользователи получили множество плюшек:
— прозрачная дозагрузка после обрыва соединения (и даже рестарта браузера!);
— очередь загрузки;
— прогресс-бар (пользователи MacOS и Safari наконец могут видеть прогресс без всяких инородных плагинов), возможность удаления файлов из очереди, если передумал.Используя File API мы можем программно, из javascript-кода:
1. получить список выбранных в диалоге файлов, их размеры и mime-типы (на которые, к слову, не стоит рассчитывать, т.к. некоторые популярные типы файлов браузеры по расширению не определяют).
2. получить необходимый диапазон байтов из файла, не загружая целиком содержимое файла в память (в отличие от Flash и Firefox 3 – см. примечание 1).
3. загрузить на сервер как целый файл, так и его кусочек.
4. загружать файлы в один drag-n-drop.
5. загружать одновременно (параллельно) несколько файлов.
Т.е. нам не нужны никакие плагины для манипуляций с файлами, и это, безусловно, очень круто!Фабула
Мы вешаем на input обработчик onchange.
Объект input поддерживает html5 атрибуты multiple для разрешения выбора нескольких файлов за раз в диалоге и accept (см. прим. 2), который производит фильтрацию файлов в диалоге согласно заданным mime-типам.
В методе onSelect пробегаемся по массиву files (который содержит сформированный браузером список выбранных файлов), выставляем дефолтные свойства и генерируем событие onSelect для каждого файла.
После этого пересоздаем кнопку, т.е. удаляем input и создаем его заново. Это делается для того, чтобы исключить повторную загрузку выбранных файлов при отправке формы на сервер в случае, когда кнопка находится внутри формы.
Инициатором начала загрузки в данном случае выступает слушатель события onSelect, вызывая метод объекта-загрузчика enqueueUpload.Метод enqueueUpload добавляет файл во внутреннюю очередь загрузчика, добавляет файл в очередь фронтенда (фронтенд — это сущность, взаимодействующая с пользователем и позволяющая ему выбирать файлы, т.е. либо input, либо плагин Flash или Silverlight) и вызывает метод startNextUpload, который либо сразу стартует загрузку этого файла, либо откладывает её, если уже одновременно загружается заданное при инициализации количество файлов.
При добавлении файла в очередь фронтенда, html5 фронтенд запускает механизм обсчета уникального хеша файла, с помощью которого [хеша] реализуется дозагрузка. Подробности можно посмотреть в статье про silverlight-загрузчик.
Да-да, хеш опять подсчитывается по алгоритму Adler32.После подсчета хеша происходит обращение к локальному хранилищу для проверки, есть ли там информация о предыдущей неудачной загрузке этого файла. Если информация находится — атрибуты файла url, sessionID и uploadedRange перезаписываются информацией из локального хранилища.
Локальное хранилище (оно же WebStorage) — это еще один элемент html5, который позволяет хранить произвольные данные в формате ключ-значение на стороне пользователя либо на время сессии (SessionStorage), либо постоянно (LocalStorage).
Когда доходит очередь до загрузки файла, вызывается метод загрузчика startUpload, который генерирует событие onStart и запускает загрузку.Метод uploadFile производит непосредственную загрузку файла на сервер.
Комментарии в коде ясно показывают неполную поддержку html5 File API в браузере Safari (по крайней мере, в OS Windows), см. прим. 3.
При возникновении ошибок запускается метод retryUpload, который повторно пытается загрузить файл указанное при инициализации загрузчика количество раз, увеличивая промежуток между попытками при каждой неудаче.
В случае исчерпания количества попыток генерируется событие onError.Для работы всего этого чуда на сервере должен быть установлен nginx с upload-модулем. Чуть подробнее об этом было написано в предыдущей статье.
Вместо послесловия.
Хочется высказать несколько мыслей:
1. На данный момент FileAPI поддерживают Chrome 8 и выше, Firefox 4 beta и частично . Про внедрение поддержки в InternetExplorer и Opera мне ничего не известно.
Однако, Chrome 8 мы отключили из-за досадного бага, из-за которого нельзя выбрать много файлов в диалоге.
Firefox 3 поддерживает FileAPI по-своему, там нет поддержки насущно необходимого объекта FormData, поэтому загрузка больших файлов невозможна, т.к. требует чтения всего содержимого файла в память компьютера.
2. Атрибут accept работает очень коряво, много mime-типов браузеры просто не понимают. Поэтому для меня остается загадкой, почему фильтрация сделана именно так, а не по списку расширений, как это сделано в Flash и Silverlight.
3. Браузер Safari не реализует объект FileReader и метод Blob.slice, поэтому в нём не работает дозагрузка средствами html5. Т.к., дозагрузка — это очень полезная «плюшка», то мы поменяли в Safari порядок вызова загрузчиков, сделав Silverlight более предпочтительным.
4. Не совсем очевидно, но при использовании битовых операций Javascript преобразует операнды к типу signed int32. А т.к. для подсчета контрольной суммы Adler32 нужны беззнаковые числа, пришлось отказаться от битового сдвига влево и использовать умножение на 65536.
5. Нужно делать URI-кодирование имени файла на клиенте и декодирование на сервере, т.к. имя попадает в заголовок Content-Disposition, а заголовки не должны по стандарту содержать не-ASCII символы.
6. Обязательно нужно предупреждать пользователей о необходимости отключения плагина Firebug или ему подобных и вот почему: Firebug на вкладке Сеть логирует всю сетевую активность и полностью сохраняет все запросы, а т.к. наши запросы небольшие по размеру, то встроенный ограничитель плагина не срабатывает и на больших файлах мы можем получить большое потребление памяти браузером.
В данной статье я хочу рассмотреть FileSystem API и File API, разобраться с его методами и показать пару полезных штук. Эта статья является компиляцией материалов с html5rocks (1, 2, 3). Все представленные ниже демки можно посмотреть по первым двум ссылкам. Третья ссылка так же предлагает ряд интересных демо. Ну а теперь займемся изучением материала.
В качестве введения
С помощью FileSystem API и File API веб приложение может создавать, читать, просматривать и записывать файлы находящиеся в области пользовательской «песочницы».
- Чтение и управление файлами: File/Blob, FileList, FileReader
- Создание и запись: BlobBuilder, FileWriter
- Работа с директориями и права доступа: DirectoryReader, FileEntry/DirectoryEntry, LocalFileSystem
- File — собственно файл; позволяет получить такую доступную только для чтения информацию, как имя, размер, mimetype и прочее.
- FileList — «массив» объектов File.
- Blob — сущность, позволяющая разбирать файл по байтам.
Поддержка браузерами и ограничение на хранение
На момент написания статьи только Google Chrome 9+ имеет рабочую реализацию FileSystem API. И на данный момент пока нет никаких диалоговых окон для управления файлами и квотами на хранилище, поэтому нужно будет использовать флаг --unlimited-quota-for-files (в случае разработки приложений для Chrome Web Store будет достаточно манифеста с разрешением unlimitedStorage). Но все меняется и пользователи в скором времени получат возможность для управления правами по работе с файлами, которые будут требоваться приложению.
Вам может потребоваться использование флага --allow-file-access-from-files , если вы дебажите приложение с использованием file://. Если этот флаг не использовать, то будут выброшены исключения типа SECURITY_ERR или QUOTA_EXCEEDED_ERR.
Обращаемся к файловой системе
Проверим поддержку браузером нужных нам функций
Веб приложение может обратиться к файловой системе (естественно в ограниченной «песочнице») вызвав следующий метод window.requestFileSystem():
Правила хранения, доступные значения window.TEMPORARY и window.PERSISTENT. данные, хранящиеся с использованием ключа TEMPORARY могут быть удаление по усмотрению браузером (например, если не хватает места). Если же выставлен ключPERSISTENT, то данные могут быть очищены только после действий пользователя или приложения.
Callback-функция, выполняемая в случае успешного обращения к файловой системе. Ее аргументом является объект типа FileSystem.
Необязательная callback-функция для обработки ошибок. Так же вызывается, когда возникают ошибки обращения к файловой системе. Параметром является объект типа FileError.
Если вы вызываете метод requestFileSystem() в рамках вашего приложения в первый раз, то в этот момент и будет создано хранилище. Очень важно помнить, что данное хранилище является закрытым и другое приложение не будет иметь к нему доступа. Это так же значит, что приложение не может менять прочие файлы и папки, расположенные на жестком диске.
Спецификация по FileSystem так же описывает API для синхронной работы, а именно интерфейс LocalFileSystemSync, который предполагается использовать совместно Web Workers. Но в этой статье данное API не будет рассмотрено.
Возвращаясь к методу requestFileSystem() стоит описать возможные варианты возникающих ошибок:
Описанный пример очень прост, но по сути является заготовкой для дальнейшей работы с возникающими ошибками.
Работа с файлам
Для работы с файлам предусмотрен интерфейс FileEntry. Он обладает рядом методов и свойств, которые мы привыкли ассоциировать с обычными файлами. Приведем их ниже:
Разберем на примерах азы работы с FileEntry.
Создание файла
Получить или создать файл можно с помощью метода getFile() у интерфейса DirectoryEntry. После обращения к хранилищу, callback возвращает нам объект FileSystem, содержащий в себе DirectoryEntry (fs.root), ссылающийся на корневую папку хранилища.
Следующий код создаст пустой файл «log.txt»:
Итак, после обращения к файловому хранилищу, у нас в руках оказывается FileSystem. Внутри сallback-функции мы можем обратится к методу fs.root.getFile(), передав имя файла, который требуется создать. Можно передать как относительный, так и абсолютный путь — главное чтобы он был верным. Например, ошибочным будет создание файла, если его родительская папка не существует. Вторым аргументом метода getFile() является объект, описывающий параметры объекта, которые будут к нему применены, если он еще не создан. Более подробно можно прочитать в документации.
Чтение файла по имени
Следующий код обращается к файлу «log.txt» и читает его содержимое с помощью FileReader API, после чего записывает все содержимое в блок . Если файл не существует, то будет выброшена ошибка.
- FileReader.readAsBinaryString(Blob|File) — результат будет содержать байтовую строку.
- FileReader.readAsText(Blob|File, opt_encoding) — результат будет содержать текстовую строку. Кодировка по умолчанию — 'UTF-8', менять можно с помощью задания опционального параметра
- FileReader.readAsDataURL(Blob|File) — на выходе имеем data URL.
- FileReader.readAsArrayBuffer(Blob|File) — получаем данные в виде ArrayBuffer.
Иногда нам может потребоваться не весь файл, а лишь его часть, для этого удобно использовать File.slice(start_byte,length).
Выглядит это так:В следующем примере мы сможем прочитать либо нужные на байты, либо весь файл целиком. Особое внимание обратите на onloadend и evt.target.readyState, которые в данном случае будут заменять нам событие onload. (О событиях чуть ниже).
- onloadstart
- onprogress
- onload,
- onabort
- onerror
- onloadend
Запись в файл
С помощью следующего кода мы создадим файл «log.txt» (если он не существует) и запишем в него 'Ipsum Lorem'.
Как видно, мы обращаемся к методу createWriter() для получения объекта. Кроме этого, мы обрабатываем события окончания записи в файл и возможного создания ошибки.
Дописываем данные в файл
Следующий код допишет 'Hello World' в конец файла. Если файла нет, то выброситься ошибка.
Создание копий выбранных файлов
Следующий код позволяет пользователю выбирать несколько файлов, используя multiple> и создает копии этих файлов.
Для упрощения выбора файлов можно использовать HTML5 Drag and Drop.Удаление файлов
Следующий код удалит 'log.txt'.
Работа с директориями
Работа с директориями осуществляется за счет использования DirectoryEntry, который обладает большинством свойств FileEntry ( они оба наследуют интерфейс Entry). Перечислим ниже свойства и методы DirectoryEntry.
Создание директорий
Для создания и обращения к директориям используется getDirectory() интерфейса DirectoryEntry. Можно передавать как имя, так и путь до директории.
Создадим в корне директорию «MyPictures»:
Поддиректории
Создание поддиректорий по сути то же самое, что и создание директорий, однако надо помнить, что если родительская директория не существует, то будет выброшена ошибка. Следующий код показывает, как можно обойти это ограничение:
Теперь у нас создана директория «music/genres/jazz» и мы можем обращаться к любому ее уровню и создавать в них новые файлы. Например:
Разбираем содержимое директории
Чтобы узнать, что содержится в директории нужно создать DirectoryReader и вызвать его метод readEntries(), но при этом нет гарантии, что вернется все содержимое выбранной директории (. ). Это значит, что надо обращаться к методу DirectoryReader.readEntries(), пока результат не станет пустым. Пример:
Удаляем директорию
Для удаления следует вызвать метод DirectoryEntry.remove(). Важно знать, что если попытаться удалить не пустую директорию, то будет выброшена ошибка.
Удалим пустую директорию «jazz» из "/music/genres/":
Рекурсивно удаляем директории
Если у вас есть не пустая директория и вы все же хотите ее удалить, то вам поможет метод removeRecursively(), который удалить и директорию и все ее содержимое.
Произведем эту операцию с директорией «music»:
Копируем, переименовываем и перемещаем
FileEntry и DirectoryEntry полностью идентичны в этом аспекте.
Копируем
И FileEntry и DirectoryEntry имеют метод copyTo() для копирования. В случае директорий, метод рекурсивно создаст и все содержимое.
Скопируем «me.jpg» из одной директории в другую:
Перемещение или переименование
У FileEntry и DirectoryEntry есть метод moveTo(), позволяющие перемещать и переименовывать файлы и директории. Первым аргументом является родительская папка, вторым (опциональным) параметром является новое имя.
Переименуем «me.jpg» в «you.jpg»:
Переместим «me.jpg» из корневой директории в «newfolder».Use Cases
HTML5 предлагает несколько вариантов локального хранения данных, но FileSystem отличается тем, что позволяет удовлетворить те потребности, которые не удовлетворяют базы данных. В основном, это нужды приложение, которые хранят большой объем бинарных данных и/или предоставляет доступ к файлам приложениям вне браузера.
Используя File API, добавленный к DOM в HTML5, в веб-приложениях теперь можно запрашивать пользователя выбрать локальные файлы и затем читать содержимое этих файлов. Выбор файлов может осуществляться с помощью элемента или drag and drop.
Если вы хотите использовать DOM File API в расширениях или коде Chrome, используйте. На самом деле, в таком случае вам необходимо ознакомиться с дополнительными нюансами. См. статью Using the DOM File API in chrome code для подробностей.
Читайте также: