Verwenden von Dateien aus Webanwendungen
Hinweis: Diese Funktion ist in Web Workers verfügbar.
Mit der File API kann Webinhalt den Benutzer bitten, lokale Dateien auszuwählen und dann den Inhalt dieser Dateien zu lesen. Diese Auswahl kann entweder durch die Verwendung eines HTML <input type="file">
Elements oder durch Drag-and-Drop erfolgen.
Zugriff auf ausgewählte Datei(en)
Betrachten Sie dieses HTML:
<input type="file" id="input" multiple />
Die File API ermöglicht den Zugriff auf ein FileList
, das File
Objekte enthält, die die vom Benutzer ausgewählten Dateien repräsentieren.
Das multiple
Attribut auf dem input
Element erlaubt es dem Benutzer, mehrere Dateien auszuwählen.
Zugriff auf die erste ausgewählte Datei mit einem klassischen DOM-Selektor:
const selectedFile = document.getElementById("input").files[0];
Zugriff auf ausgewählte Datei(en) bei einem Änderungsereignis
Es ist auch möglich (aber nicht erforderlich), auf das FileList
über das change
Ereignis zuzugreifen. Sie müssen EventTarget.addEventListener()
verwenden, um den change
Ereignis-Listener hinzuzufügen, wie hier gezeigt:
const inputElement = document.getElementById("input");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
const fileList = this.files; /* now you can work with the file list */
}
Informationen zu ausgewählten Datei(en) abrufen
Das vom DOM bereitgestellte FileList
Objekt listet alle vom Benutzer ausgewählten Dateien auf, jede als ein File
Objekt angegeben. Sie können ermitteln, wie viele Dateien der Benutzer ausgewählt hat, indem Sie den Wert des length
Attributs der Dateiliste überprüfen:
const numFiles = fileList.length;
Individuelle File
Objekte können durch Zugreifen auf die Liste als ein Array abgerufen werden.
Es gibt drei Attribute, die vom File
Objekt bereitgestellt werden und nützliche Informationen über die Datei enthalten.
Beispiel: Anzeige der Dateigröße(n)
Das folgende Beispiel zeigt eine mögliche Verwendung der size
Eigenschaft:
<form name="uploadForm">
<div>
<input id="uploadInput" type="file" multiple />
<label for="fileNum">Selected files:</label>
<output id="fileNum">0</output>;
<label for="fileSize">Total size:</label>
<output id="fileSize">0</output>
</div>
<div><input type="submit" value="Send file" /></div>
</form>
const uploadInput = document.getElementById("uploadInput");
uploadInput.addEventListener(
"change",
() => {
// Calculate total size
let numberOfBytes = 0;
for (const file of uploadInput.files) {
numberOfBytes += file.size;
}
// Approximate to the closest prefixed unit
const units = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
const exponent = Math.min(
Math.floor(Math.log(numberOfBytes) / Math.log(1024)),
units.length - 1,
);
const approx = numberOfBytes / 1024 ** exponent;
const output =
exponent === 0
? `${numberOfBytes} bytes`
: `${approx.toFixed(3)} ${units[exponent]} (${numberOfBytes} bytes)`;
document.getElementById("fileNum").textContent = uploadInput.files.length;
document.getElementById("fileSize").textContent = output;
},
false,
);
Verwenden versteckter Datei-Eingabefelder mit der Methode click()
Sie können das zugegebenermaßen unschöne <input>
Element für Dateien ausblenden und eine eigene Schnittstelle für das Öffnen des Dateiauswahl-Dialogs und die Anzeige der vom Benutzer ausgewählten Datei oder Dateien präsentieren. Das können Sie tun, indem Sie das Eingabeelement mit display:none
stylen und dann die click()
Methode auf dem <input>
Element aufrufen.
Betrachten Sie dieses HTML:
<input type="file" id="fileElem" multiple accept="image/*" />
<button id="fileSelect" type="button">Select some files</button>
#fileElem {
display: none;
}
Der Code, der das click
Ereignis behandelt, kann so aussehen:
const fileSelect = document.getElementById("fileSelect");
const fileElem = document.getElementById("fileElem");
fileSelect.addEventListener(
"click",
(e) => {
if (fileElem) {
fileElem.click();
}
},
false,
);
Sie können das <button>
Element nach Belieben stylen.
Verwenden eines Label-Elements zum Auslösen eines versteckten Datei-Eingabefelds
Um das Öffnen des Dateiauswahl-Dialogs ohne Verwendung von JavaScript (Methode click()) zu ermöglichen, kann ein <label>
Element verwendet werden. Beachten Sie, dass in diesem Fall das Eingabeelement nicht mit display: none
(noch mit visibility: hidden
) versteckt werden darf, da das Label ansonsten nicht über die Tastatur zugänglich wäre. Verwenden Sie stattdessen die visually-hidden technique.
Betrachten Sie dieses HTML:
<input
type="file"
id="fileElem"
multiple
accept="image/*"
class="visually-hidden" />
<label for="fileElem">Select some files</label>
und dieses CSS:
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
input.visually-hidden:is(:focus, :focus-within) + label {
outline: thin dotted;
}
Es ist nicht erforderlich, JavaScript-Code hinzuzufügen, um fileElem.click()
aufzurufen. Auch in diesem Fall können Sie das Label-Element nach Belieben stylen. Sie müssen einen visuellen Hinweis auf den Fokusstatus des versteckten Eingabefeldes auf dessen Label bereitstellen, sei es ein Umriss wie oben gezeigt oder eine Hintergrundfarbe oder ein Schatten. (Zum Zeitpunkt der Erstellung zeigt Firefox diesen visuellen Hinweis für <input type="file">
Elemente nicht an.)
Auswählen von Dateien per Drag-and-Drop
Sie können dem Benutzer auch erlauben, Dateien in Ihre Webanwendung zu ziehen und abzulegen.
Der erste Schritt ist die Einrichtung einer Ablagezone. Welche Teile Ihres Inhalts das Ablegen von Dateien akzeptieren, kann je nach Design Ihrer Anwendung variieren, aber ein Element so zu machen, dass es Drop-Ereignisse erhält, ist einfach:
let dropbox;
dropbox = document.getElementById("dropbox");
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);
In diesem Beispiel verwandeln wir das Element mit der ID dropbox
in unsere Ablagezone. Dies geschieht durch das Hinzufügen von Listeners für die Ereignisse dragenter
, dragover
und drop
.
Wir müssen bei den dragenter
und dragover
Ereignissen in unserem Fall tatsächlich nichts tun, daher sind diese Funktionen einfach. Sie stoppen lediglich die Weitergabe des Ereignisses und verhindern, dass die Standardaktion ausgeführt wird:
function dragenter(e) {
e.stopPropagation();
e.preventDefault();
}
function dragover(e) {
e.stopPropagation();
e.preventDefault();
}
Die eigentliche Magie passiert in der drop()
Funktion:
function drop(e) {
e.stopPropagation();
e.preventDefault();
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
Hier holen wir das dataTransfer
Feld aus dem Ereignis, ziehen die Dateiliste daraus und übergeben diese an handleFiles()
. Von diesem Punkt an ist die Bearbeitung der Dateien gleich, ob der Benutzer das input
Element oder Drag-and-Drop verwendet hat.
Beispiel: Thumbnails von benutzergewählten Bildern anzeigen
Angenommen, Sie entwickeln die nächste große Foto-Sharing-Website und möchten HTML verwenden, um Thumbnail-Vorschauen von Bildern anzuzeigen, bevor der Benutzer sie tatsächlich hochlädt. Sie können Ihr Eingabeelement oder Ihre Ablagezone wie zuvor besprochen einrichten und eine Funktion wie die handleFiles()
Funktion unten aufrufen lassen.
function handleFiles(files) {
for (const file of files) {
if (!file.type.startsWith("image/")) {
continue;
}
const 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.
const reader = new FileReader();
reader.onload = (e) => {
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
}
Hier prüft unsere Schleife, die die benutzerausgewählten Dateien verarbeitet, für jede Datei das type
Attribut, ob ihr MIME-Typ mit image/
beginnt. Für jede Datei, die ein Bild ist, erstellen wir ein neues img
Element. CSS kann verwendet werden, um hübsche Ränder oder Schatten und die Größe des Bildes festzulegen, sodass dies hier nicht nötig ist.
Jedes Bild erhält die CSS-Klasse obj
, was es leicht macht, es im DOM-Baum zu finden. Wir fügen jedem Bild auch ein file
Attribut hinzu, das die entsprechende File
für das Bild bestimmt; dies wird uns ermöglichen, die Bilder später tatsächlich hochzuladen. Wir verwenden Node.appendChild()
, um das neue Thumbnail zum Vorschaubereich unseres Dokuments hinzuzufügen.
Als Nächstes richten wir den FileReader
ein, um das asynchrone Laden des Bildes zu übernehmen und es dem img
Element hinzuzufügen. Nach dem Erstellen des neuen FileReader
Objekts setzen wir seine onload
Funktion und rufen dann readAsDataURL()
auf, um den Lesevorgang im Hintergrund zu starten. Wenn der gesamte Inhalt der Bilddatei geladen ist, werden sie in eine data:
URL umgewandelt, die an den onload
Rückruf übergeben wird. Unsere Implementierung dieser Routine setzt das src
Attribut des img
Elements auf das geladene Bild, wodurch das Bild im Thumbnail auf dem Bildschirm des Benutzers angezeigt wird.
Verwendung von Objekt-URLs
Die DOM-Methoden URL.createObjectURL()
und URL.revokeObjectURL()
erlauben Ihnen, einfache URL-Strings zu erstellen, die verwendet werden können, um auf alle Daten zu verweisen, die mit einem DOM File
Objekt referenziert werden können, einschließlich lokaler Dateien auf dem Computer des Benutzers.
Wenn Sie ein File
Objekt haben, auf das Sie per URL aus HTML verweisen möchten, können Sie eine Objekt-URL dafür wie folgt erstellen:
const objectURL = window.URL.createObjectURL(fileObj);
Die Objekt-URL ist ein String, der das File
Objekt identifiziert. Jedes Mal, wenn Sie URL.createObjectURL()
aufrufen, wird eine eindeutige Objekt-URL erstellt, selbst wenn Sie bereits eine Objekt-URL für diese Datei erstellt haben. Jede dieser muss freigegeben werden. Während sie automatisch freigegeben werden, wenn das Dokument entladen wird, sollten Sie, wenn Ihre Seite sie dynamisch verwendet, sie explizit freigeben, indem Sie URL.revokeObjectURL()
aufrufen:
URL.revokeObjectURL(objectURL);
Beispiel: Verwenden von Objekt-URLs zur Anzeige von Bildern
Dieses Beispiel verwendet Objekt-URLs, um Bild-Thumbnails anzuzeigen. Darüber hinaus zeigt es andere Dateiinformationen einschließlich ihrer Namen und Größen an.
Das HTML, das die Schnittstelle präsentiert, sieht so aus:
<input type="file" id="fileElem" multiple accept="image/*" />
<a href="#" id="fileSelect">Select some files</a>
<div id="fileList">
<p>No files selected!</p>
</div>
#fileElem {
display: none;
}
Dies etabliert unser Datei <input>
Element sowie einen Link, der den Dateiauswahl-Dialog aufruft (da wir das Datei-Eingabefeld verstecken, um zu verhindern, dass jene wenig attraktive Benutzeroberfläche angezeigt wird). Dies wird im Abschnitt Verwenden versteckter Datei-Eingabefelder mit der Methode click() erklärt, ebenso wie die Methode, die den Dateiauswahl-Dialog aufruft.
Die handleFiles()
Methode folgt:
const fileSelect = document.getElementById("fileSelect"),
fileElem = document.getElementById("fileElem"),
fileList = document.getElementById("fileList");
fileSelect.addEventListener(
"click",
(e) => {
if (fileElem) {
fileElem.click();
}
e.preventDefault(); // prevent navigation to "#"
},
false,
);
fileElem.addEventListener("change", handleFiles, false);
function handleFiles() {
fileList.textContent = "";
if (!this.files.length) {
const p = document.createElement("p");
p.textContent = "No files selected!";
fileList.appendChild(p);
} else {
const list = document.createElement("ul");
fileList.appendChild(list);
for (const file of this.files) {
const li = document.createElement("li");
list.appendChild(li);
const img = document.createElement("img");
img.src = URL.createObjectURL(file);
img.height = 60;
li.appendChild(img);
const info = document.createElement("span");
info.textContent = `${file.name}: ${file.size} bytes`;
li.appendChild(info);
}
}
}
Dies beginnt mit dem Abrufen der URL des <div>
mit der ID fileList
. Dies ist der Block, in den wir unsere Dateiliste einschließlich Thumbnails einfügen werden.
Wenn das FileList
Objekt, das an handleFiles()
übergeben wird, leer ist, setzen wir das innere HTML des Blocks, um "Keine Dateien ausgewählt!" anzuzeigen. Andernfalls beginnen wir, unsere Dateiliste wie folgt zu erstellen:
- Ein neues ungeordnetes Listen-(
<ul>
) Element wird erstellt. - Das neue Listen-Element wird in den
<div>
Block eingefügt, indem seineNode.appendChild()
Methode aufgerufen wird. - Für jede
File
in derFileList
, die durchfiles
repräsentiert wird:- Ein neues Listenelement (
<li>
) wird erstellt und in die Liste eingefügt. - Ein neues Bild- (
<img>
) Element wird erstellt. - Der Quellcode des Bildes wird auf eine neue Objekt-URL gesetzt, die die Datei repräsentiert, indem
URL.createObjectURL()
verwendet wird, um die Blob-URL zu erstellen. - Die Höhe des Bildes wird auf 60 Pixel gesetzt.
- Das neue Listenelement wird der Liste hinzugefügt.
- Ein neues Listenelement (
Hier ist eine Live-Demo des oben genannten Codes:
Beachten Sie, dass wir die Objekt-URL nicht sofort widerrufen, nachdem das Bild geladen wurde, da dies das Bild unbrauchbar für Benutzerinteraktionen machen würde (wie Rechtsklick zum Speichern des Bildes oder Öffnen in einem neuen Tab). Für langlebige Anwendungen sollten Sie Objekt-URLs widerrufen, wenn sie nicht mehr benötigt werden (wie wenn das Bild aus dem DOM entfernt wird), um Speicher freizugeben, indem die URL.revokeObjectURL()
Methode aufgerufen wird und die Objekt-URL übergeben wird.
Beispiel: Hochladen einer vom Benutzer ausgewählten Datei
Dieses Beispiel zeigt, wie der Benutzer Dateien (wie die im vorherigen Beispiel ausgewählten Bilder) auf einen Server hochladen kann.
Hinweis:
Es wird in der Regel bevorzugt, HTTP-Anfragen mit der Fetch API anstelle von XMLHttpRequest
durchzuführen. In diesem Fall möchten wir dem Benutzer jedoch den Upload-Fortschritt anzeigen, und diese Funktion wird von der Fetch API noch nicht unterstützt, deshalb wird das Beispiel XMLHttpRequest
verwendet.
Die Arbeit zur Verfolgung der Standardisierung von Fortschrittsbenachrichtigungen mit der Fetch API ist unter https://github.com/whatwg/fetch/issues/607.
Erstellen der Upload-Aufgaben
Fortsetzend mit dem Code, der die Thumbnails im vorherigen Beispiel erstellte, denken Sie daran, dass jedes Thumbnail-Bild in der CSS-Klasse obj
mit der entsprechenden File
in einem file
Attribut angehängt ist. Dies ermöglicht es uns, alle Bilder auszuwählen, die der Benutzer zum Hochladen ausgewählt hat, unter Verwendung von Document.querySelectorAll()
, wie folgt:
function sendFiles() {
const imgs = document.querySelectorAll(".obj");
for (const img of imgs) {
new FileUpload(img, img.file);
}
}
document.querySelectorAll
ruft eine NodeList
von allen Elementen im Dokument mit der CSS-Klasse obj
ab. In unserem Fall werden dies alle Bildthumbnails sein. Sobald wir diese Liste haben, ist es trivial, sie durchzugehen und eine neue FileUpload
Instanz für jede zu erstellen. Jede dieser Instanzen übernimmt den Upload der entsprechenden Datei.
Verarbeitung des Upload-Vorgangs für eine Datei
Die FileUpload
Funktion akzeptiert zwei Eingaben: ein Bildelement und eine Datei, aus der die Bilddaten gelesen werden sollen.
function FileUpload(img, file) {
const reader = new FileReader();
this.ctrl = createThrobber(img);
const xhr = new XMLHttpRequest();
this.xhr = xhr;
this.xhr.upload.addEventListener(
"progress",
(e) => {
if (e.lengthComputable) {
const percentage = Math.round((e.loaded * 100) / e.total);
this.ctrl.update(percentage);
}
},
false,
);
xhr.upload.addEventListener(
"load",
(e) => {
this.ctrl.update(100);
const canvas = this.ctrl.ctx.canvas;
canvas.parentNode.removeChild(canvas);
},
false,
);
xhr.open(
"POST",
"https://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php",
);
xhr.overrideMimeType("text/plain; charset=x-user-defined-binary");
reader.onload = (evt) => {
xhr.send(evt.target.result);
};
reader.readAsBinaryString(file);
}
function createThrobber(img) {
const throbberWidth = 64;
const throbberHeight = 6;
const throbber = document.createElement("canvas");
throbber.classList.add("upload-progress");
throbber.setAttribute("width", throbberWidth);
throbber.setAttribute("height", throbberHeight);
img.parentNode.appendChild(throbber);
throbber.ctx = throbber.getContext("2d");
throbber.ctx.fillStyle = "orange";
throbber.update = (percent) => {
throbber.ctx.fillRect(
0,
0,
(throbberWidth * percent) / 100,
throbberHeight,
);
if (percent === 100) {
throbber.ctx.fillStyle = "green";
}
};
throbber.update(0);
return throbber;
}
Die oben gezeigte FileUpload()
Funktion erstellt einen Lader, der zur Anzeige von Fortschrittsinformationen verwendet wird, und erstellt dann ein XMLHttpRequest
, um den Upload der Daten zu handhaben.
Bevor die Daten tatsächlich übertragen werden, werden mehrere vorbereitende Schritte unternommen:
- Der
progress
Listener desXMLHttpRequest
Uploads wird gesetzt, um den Lader mit neuen Prozentinformationen zu aktualisieren, sodass der Lader während des Uploads basierend auf den neuesten Informationen aktualisiert wird. - Der
load
Ereignis-Handler desXMLHttpRequest
Uploads wird gesetzt, um die Fortschrittsinformationen des Laders auf 100 % zu aktualisieren, um sicherzustellen, dass der Fortschrittsanzeiger tatsächlich 100 % erreicht (im Falle von Granularitätsbesonderheiten während des Prozesses). Er entfernt dann den Lader, da dieser nicht mehr benötigt wird. Dadurch verschwindet der Lader, wenn der Upload abgeschlossen ist. - Die Anfrage zum Hochladen der Bilddatei wird durch Aufrufen der
open()
Methode desXMLHttpRequest
mởóduloši gestartet. - Der MIME-Typ für den Upload wird durch Aufrufen der
overrideMimeType()
Funktion desXMLHttpRequest
gesetzt. In diesem Fall verwenden wir einen generischen MIME-Typ; je nach Ihrem Anwendungsfall kann es sein, dass Sie den MIME-Typ überhaupt nicht setzen müssen. - Das
FileReader
Objekt wird verwendet, um die Datei in einen Binärstring zu konvertieren. - Schließlich wird, wenn der Inhalt geladen ist, die
send()
Funktion desXMLHttpRequest
aufgerufen, um den Inhalt der Datei hochzuladen.
Asynchrone Verarbeitung des Upload-Vorgangs
Dieses Beispiel, das PHP auf der Serverseite und JavaScript auf der Clientseite verwendet, zeigt das asynchrone Hochladen einer Datei.
<?php
if (isset($_FILES["myFile"])) {
// Example:
move_uploaded_file($_FILES["myFile"]["tmp_name"], "uploads/" . $_FILES["myFile"]["name"]);
exit;
}
?><!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>dnd binary upload</title>
<script>
function sendFile(file) {
const uri = "/index.php";
const xhr = new XMLHttpRequest();
const fd = new FormData();
xhr.open("POST", uri, true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText); // handle response.
}
};
fd.append("myFile", file);
// Initiate a multipart/form-data upload
xhr.send(fd);
}
window.onload = () => {
const dropzone = document.getElementById("dropzone");
dropzone.ondragover = dropzone.ondragenter = (event) => {
event.stopPropagation();
event.preventDefault();
};
dropzone.ondrop = (event) => {
event.stopPropagation();
event.preventDefault();
const filesArray = event.dataTransfer.files;
for (let 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>
Beispiel: Verwenden von Objekt-URLs zur Anzeige von PDFs
Objekt-URLs können für andere Dinge als nur Bilder verwendet werden! Sie können verwendet werden, um eingebettete PDF-Dateien oder andere Ressourcen anzuzeigen, die vom Browser angezeigt werden können.
In Firefox muss die Präferenz pdfjs.disabled
auf false
gesetzt sein, damit das PDF im iframe eingebettet angezeigt wird (anstatt als herunterladbare Datei vorgeschlagen zu werden).
<iframe id="viewer"></iframe>
Und hier ist die Änderung des src
Attributs:
const objURL = URL.createObjectURL(blob);
const iframe = document.getElementById("viewer");
iframe.setAttribute("src", objURL);
// Later:
URL.revokeObjectURL(objURL);
Beispiel: Verwenden von Objekt-URLs mit anderen Dateitypen
Sie können Dateien anderer Formate auf dieselbe Weise manipulieren. Hier wird gezeigt, wie hochgeladene Videos angezeigt werden:
const video = document.getElementById("video");
const objURL = URL.createObjectURL(blob);
video.src = objURL;
video.play();
// Later:
URL.revokeObjectURL(objURL);