Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.

Используя File API, добавленный к DOM в HTML5, в веб-приложениях теперь можно запрашивать пользователя выбрать локальные файлы и затем читать содержимое этих файлов. Выбор файлов может осуществляться с помощью элемента <input> или drag and drop.

Если вы хотите использовать DOM File API в расширениях или коде Chrome, используйте. На самом деле, в таком случае вам необходимо ознакомиться с дополнительными нюансами. См. статью Using the DOM File API in chrome code для подробностей.

Доступ к выбранным файлам

Рассмотрим следующий код:

<input type="file" id="input">

File API делает возможным доступ к FileList, который содержит объекты File, которым соответствуют файлы, загруженные пользователем.

Если пользователь выбирает только один файл, ему соответствует первый файл в списке.

Обращение к одному выбранному файлу с использованием классической DOM-модели:

var selectedFile = document.getElementById('input').files[0];

Обращение к одному выбранному файлу через jQuery:

var selectedFile = $('#input').get(0).files[0];

var selectedFile = $('#input')[0].files[0];

Если вы получили ошибку "files is undefined", значит вы выбрали не один HTML-элемент, а список элементов, который возвращает jQuery. Нужно уточнить, у какого именно элемента необходимо вызвать метод "files"

Обращение к выбранным файлам через событие change

Также возможно (но не обязательно) получить доступ к FileList, исользуя событие change:

<input type="file" id="input" onchange="handleFiles(this.files)">

Когда пользователь выбирает файл, функция handleFiles() будет вызвана с объектом FileList, который состоит из объектов File, представляющих файлы, выбранные пользователем.

Если вы хотите дать возможность пользователю выбирать несколько файлов, просто добавьте атрибут multiple для input-элемента:

<input type="file" id="input" multiple onchange="handleFiles(this.files)">

В этом случае, список файлов, передаваемый в handleFiles(), содержит объекты  File  для каждого выбранного пользователем файла.

Динамическое добавление обработчика события change

Если ваш input-элемент был создан с помощью Javascript библиотеки, например такой, как jQuery, то для добавления обработчика события change необходимо использовать конструкцию element.addEventListener(), как показано в следующем примере:

var inputElement = document.getElementById("input");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
  var fileList = this.files; /* теперь вы можете работь со списком файлов */
}

Обратите внимание, что в данном случае, функция handleFiles() сама является обработчиком события, в отличие от предыдущих примеров, где был вызван обработчик событий, который передавал его параметр.

Получение информации о выделенных файлах

Объект FileList предоставляемый классическим DOM содержит все файлы выбранные пользователем, каждый из которых представляет собой объект File. Вы можете определить сколько файлов выбрал пользователь проверяя значение аттрибута длинны (length) списка файлов:

var numFiles = files.length;

Конкретные объекты File могут быть получены обращением к списку файлов как к массиву:

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

Этот цикл проходит по всем файлам в списке файлов.

Всего существует три аттрибута, предоставляемых объектом File, которые содержат полезную информацию о файле.

name
Имя файла как строка доступная только для чтения. Это просто имя файла и оно не включает в себя информацию о пути.
size
Размер файла в байтах, как 64-битное целое число (возможно только чтение).
type
MIME тип файла, как строка доступная только для чтения, или пустая строка ("") если тип файла не может быть определён.

Пример: Отображение размера файла(ов)

Следующий пример показывает возможное использование свойства size:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>File(s) size</title>
<script>
function updateSize() {
  var nBytes = 0,
      oFiles = document.getElementById("uploadInput").files,
      nFiles = oFiles.length;
  for (var nFileId = 0; nFileId < nFiles; nFileId++) {
    nBytes += oFiles[nFileId].size;
  }
  var sOutput = nBytes + " bytes";
  // optional code for multiples approximation
  for (var aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
    sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
  }
  // end of optional code
  document.getElementById("fileNum").innerHTML = nFiles;
  document.getElementById("fileSize").innerHTML = sOutput;
}
</script>
</head>

<body onload="updateSize();">
<form name="uploadForm">
<p><input id="uploadInput" type="file" name="myFiles" onchange="updateSize();" multiple> selected files: <span id="fileNum">0</span>; total size: <span id="fileSize">0</span></p>
<p><input type="submit" value="Send file"></p>
</form>
</body>
</html>

Использование метода click() скрытых элементов выбора файла

Начиная с Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1), вы можете скрыть непривлекательный элемент <input> и предоставить свой собственный интерфейс для открытия диалогового окна выбора и отображения файла или файлов, выбранных пользователем. Вы можете сделать это, присвоив свойству display элемента input  значение none (display:none) и вызывая метод click() скрытого элемента <input>.

Рассмотрим следующую разметку HTML:

<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<a href="#" id="fileSelect">Select some files</a>

Код, обрабатывающий событие click, может выглядеть следующим образом:

var fileSelect = document.getElementById("fileSelect"),
  fileElem = document.getElementById("fileElem");

fileSelect.addEventListener("click", function (e) {
  if (fileElem) {
    fileElem.click();
  }
  e.preventDefault(); // предотвращает перемещение к "#"
}, false);

Таким образом, вы можете стилизовать новую кнопку открытия диалога выбора файла так, как пожелаете.

Использование элемента label скрытого элемента input

Для того, чтобы открыть диалог выбора файла без использования JavaScript (метода click()), можно воспользоваться элементом <label>.

Рассмотрим следующую разметку HTML:

<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)"> 
<label for="fileElem">Select some files</label>

В данном случае нет необходимости добавлять код JavaScript для того, чтобы вызвать fileElem.click(). Также в данном случае вы можете стилизовать элемент label так, как пожелаете.

Выбор файлов с использованием технологии drag and drop

Также вы можете предоставить пользователю возможность непосредственно перетаскивать файлы в ваше веб-приложение.

На первом шаге необходимо определить зону, в которую будут перетаскиваться файлы. В каждом конкретном случае часть содержимого вашей страницы, ответственная за приемку перетаскиваемых файлов, может варьироваться в зависимости от дизайна приложения, тем не менее, заставить элемент воспринимать события перетаскивания достаточно просто:

var dropbox;

dropbox = document.getElementById("dropbox");
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);

В данном примере мы превращаем элемент с ID, равным dropbox, в нашу зону перетаскивания при помощи добавления обработчиков для событий dragenter, dragover и drop.

В нашем случае нет необходимости делать что-то особенное при обработке событий dragenter и dragover, таким образом, обе функции, ответственные за обработку данных событий, довольно просты. Они останавливают распространение события и предотвращают возникновение действия по умолчанию:

function dragenter(e) {
  e.stopPropagation();
  e.preventDefault();
}

function dragover(e) {
  e.stopPropagation();
  e.preventDefault();
} 

Вся настоящая магия происходит в функции drop():

function drop(e) {
  e.stopPropagation();
  e.preventDefault();

  var dt = e.dataTransfer;
  var files = dt.files;

  handleFiles(files);
}

Здесь мы извлекаем из события поле dataTransfer, затем вытаскиваем из него список файлов и передаем этот список в handleFiles(). После этого процесс обработки файлов одинаков вне зависимости от того, использовал ли пользователь для их выбора элемент input или технологию drag and drop.

Example: Showing thumbnails of user-selected images

Let's say you're developing the next great photo-sharing web site, and want to use HTML5 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.

function handleFiles(files) {
  for (var i = 0; i < files.length; i++) {
    var file = files[i];
    var imageType = /^image\//;
    
    if (!imageType.test(file.type.match)) {
      continue;
    }
    
    var img = document.createElement("img");
    img.classList.add("obj");
    img.file = file;
    preview.appendChild(img); // Assuming that "preview" is the div output where the content will be displayed.
    
    var reader = new FileReader();
    reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
    reader.readAsDataURL(file);
  }
}

Here our loop handling the user-selected files looks at each file's type attribute to see if it's an image file (by doing a regular expression match on 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, shadows, and to specify the size of the image, so that doesn't even need to be done here.

Each image has the CSS class obj added to it, to make them 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 actually uploading later. Finally, we use Node.appendChild() to add the new thumbnail to the preview area of our document.

Then we establish the FileReader to handle actually asynchronously loading the image and attaching it to the img element. After creating the new FileReader object, we set up its onload function, 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 simply 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.

Using object URLs

Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) introduces support for the DOM window.URL.createObjectURL() and window.URL.revokeObjectURL() methods. These 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:

var objectURL = window.URL.createObjectURL(fileObj);

The object URL is a string identifying the File object. Each time you call window.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 window.URL.revokeObjectURL():

window.URL.revokeObjectURL(objectURL);

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. You can view the example live.

The HTML that presents the interface looks like this:

<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<a href="#" id="fileSelect">Select some files</a> 
<div id="fileList">
  <p>No files selected!</p>
</div>

This establishes our file <input> element, as well as a link that invokes the file picker, since we keep the file input hidden to prevent that less-than-attractive UI from being displayed. This is explained in the section Using hidden file input elements using the click() method, as is the method that invokes the file picker.

The handleFiles() method follows:

window.URL = window.URL || window.webkitURL;

var fileSelect = document.getElementById("fileSelect"),
    fileElem = document.getElementById("fileElem"),
    fileList = document.getElementById("fileList");

fileSelect.addEventListener("click", function (e) {
  if (fileElem) {
    fileElem.click();
  }
  e.preventDefault(); // prevent navigation to "#"
}, false);

function handleFiles(files) {
  if (!files.length) {
    fileList.innerHTML = "<p>No files selected!</p>";
  } else {
    var list = document.createElement("ul");
    for (var i = 0; i < files.length; i++) {
      var li = document.createElement("li");
      list.appendChild(li);
      
      var img = document.createElement("img");
      img.src = window.URL.createObjectURL(files[i]);
      img.height = 60;
      img.onload = function() {
        window.URL.revokeObjectURL(this.src);
      }
      li.appendChild(img);
      var info = document.createElement("span");
      info.innerHTML = files[i].name + ": " + files[i].size + " bytes";
      li.appendChild(info);
    }
  }
}

This starts by fetching the URL of the <div> with the ID fileList. This is the block into which we'll insert our file list, including thumbmails.

If the FileList object passed to handleFiles() is null, we simply set the inner HTML of the block to display "No files selected!". Otherwise, we start building our file list, as follows:

  1. A new unordered list (<ul>) element is created.
  2. The new list element is inserted into the <div> block by calling its element.appendChild() method.
  3. For each File in the FileList represented by files:
    1. Create a new list item (<li>) element and insert it into the list.
    2. Create a new image (<img>) element.
    3. Set the image's source to a new object URL representing the file, using window.URL.createObjectURL() to create the blob URL.
    4. Set the image's height to 60 pixels.
    5. 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 window.URL.revokeObjectURL() method, passing in the object URL string as specified by img.src.
    6. Append the new list item to the list.

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.

Creating the upload tasks

Continuing with the code that builds 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 lets us very easily select all the images the user has chosen for uploading using Document.querySelectorAll(), like this:

function sendFiles() {
  var imgs = document.querySelectorAll(".obj");
  
  for (var i = 0; i < imgs.length; i++) {
    new FileUpload(imgs[i], imgs[i].file);
  }
}

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 the image thumbnails. Once we have that list, it's trivial to go through the list, creating a new FileUpload instance for each. Each of these handles uploading the corresponding file.

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.

function FileUpload(img, file) {
  var reader = new FileReader();  
  this.ctrl = createThrobber(img);
  var xhr = new XMLHttpRequest();
  this.xhr = xhr;
  
  var self = this;
  this.xhr.upload.addEventListener("progress", function(e) {
        if (e.lengthComputable) {
          var percentage = Math.round((e.loaded * 100) / e.total);
          self.ctrl.update(percentage);
        }
      }, false);
  
  xhr.upload.addEventListener("load", function(e){
          self.ctrl.update(100);
          var canvas = self.ctrl.ctx.canvas;
          canvas.parentNode.removeChild(canvas);
      }, false);
  xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
  xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
  reader.onload = function(evt) {
    xhr.sendAsBinary(evt.target.result);
  };
  reader.readAsBinaryString(file);
}

The FileUpload() function shown above creates a throbber, which is used to display progress information, then creates an XMLHttpRequest to handle uploading the data.

Before actually transferring the data, several preparatory steps are taken:

  1. The XMLHttpRequest's upload progress listener is set to update the throbber with new percentage information, so that as the upload progresses, the throbber will be updated based on the latest information.
  2. The XMLHttpRequest's upload load event handler is set to update the throbber with 100% as the progress information (to ensure the progress indicator actually reaches 100%, in case of granularity quirks during the process). It then removes the throbber, since it's no longer needed. This causes the throbber to disappear once the upload is complete.
  3. The request to upload the image file is opened by calling XMLHttpRequest's open() method to start generating a POST request.
  4. The MIME type for the upload is set by calling the XMLHttpRequest function overrideMimeType(). In this case, we're using a generic MIME type; you may or may not need to set the MIME type at all, depending on your use case.
  5. The FileReader object is used to convert the file to a binary string.
  6. Finally, when the content is loaded the XMLHttpRequest function sendAsBinary() is called to upload the file's content.
Note: The non-standard sendAsBinary method in the example above is considered deprecated as of Gecko 31 (Firefox 31 / Thunderbird 31 / SeaMonkey 2.28) and will be removed soon. The standard send(Blob data) method can be used instead.

Handling the upload process for a file, asynchronously

<?php
if (isset($_FILES['myFile'])) {
    // Example:
    move_uploaded_file($_FILES['myFile']['tmp_name'], "uploads/" . $_FILES['myFile']['name']);
    exit;
}
?><!DOCTYPE html>
<html>
<head>
    <title>dnd binary upload</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script type="text/javascript">
        function sendFile(file) {
            var uri = "/index.php";
            var xhr = new XMLHttpRequest();
            var fd = new FormData();
            
            xhr.open("POST", uri, true);
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    // Handle response.
                    alert(xhr.responseText); // handle response.
                }
            };
            fd.append('myFile', file);
            // Initiate a multipart/form-data upload
            xhr.send(fd);
        }

        window.onload = function() {
            var dropzone = document.getElementById("dropzone");
            dropzone.ondragover = dropzone.ondragenter = function(event) {
                event.stopPropagation();
                event.preventDefault();
            }
    
            dropzone.ondrop = function(event) {
                event.stopPropagation();
                event.preventDefault();

                var filesArray = event.dataTransfer.files;
                for (var i=0; i<filesArray.length; i++) {
                    sendFile(filesArray[i]);
                }
            }
        }
    </script>
</head>
<body>
    <div>
        <div id="dropzone" style="margin:30px; width:500px; height:300px; border:1px dotted grey;">Drag & drop your file here...</div>
    </div>
</body>
</html>

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, and not proposed as a downloaded file, the preference pdfjs.disabled must be set to false .

<iframe id="viewer">

And here is the change of the src attribute:

var obj_url = window.URL.createObjectURL(blob);
var iframe = document.getElementById('viewer');
iframe.setAttribute('src', obj_url);
window.URL.revokeObjectURL(obj_url);

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:

var video = document.getElementById('video');
var obj_url = window.URL.createObjectURL(blob);
video.src = obj_url;
video.play()
window.URL.revokeObjectURL(obj_url);

Specifications

See also

Метки документа и участники

 Внесли вклад в эту страницу: chrisdavidmills, Coshrurnina, 13af, Sub-hero, L-st, nirta, Lambrusco.pro
 Обновлялась последний раз: Coshrurnina,