UNPKG

event-to-files

Version:

Get the files from an input change event or from a file or directory drag and drop event

130 lines (122 loc) 4.41 kB
/** `getFiles` returns the list of files from a `change` or `drop` Event. @remarks `getFiles` supports only the `change` event type on file inputs and the `drop` event type (`DragEvent`). Calling `getFiles` with other types of events will return an empty list. For directory drop events, `getFiles` relies on the `webkitGetAsEntry` API; if this API is unavailable in the browser, `getFiles` will return an empty list. @param event - The `change` or `drop` `Event` for which files should be retrieved. @returns A list of `EventFile`s that contain the `File`s retrieved from an `Event`. @example ```typescript import { getFiles } from "event-to-files"; // For an `<input type="file" />`. const fileInput = document.getElementById("file-input"); fileInput.addEventListener("change", async (event) => { event.preventDefault(); const files = await getFiles(event); for (const { file } of files) { console.log(file.name); } }); // For a drag and drop event. const dropZone = document.getElementById("drop-zone"); dropZone.addEventListener("dragover", (event) => { // Cancel the `dragover` event to let the `drop` event fire later. event.preventDefault(); }); dropZone.addEventListener("drop", async (event) => { event.preventDefault(); const files = await getFiles(event); for (const { file, entry } of files) { console.log(file.name); // If a directory was dragged and dropped, // we can get the file's full path in the directory. if (entry?.fullPath) { console.log(entry.fullPath); } } }); ``` */ export let getFiles = async (event) => { // Get the list of files from the `<input type="file" />` `change` event. if (event.type === "change") { return [...(event.target?.files ?? [])].map((file) => ({ file })); } // Get the list of files from the drag and drop `DragEvent`. if (event.type === "drop") { return (await Promise.all( // Get the data transfer items of kind `file` and read the files. [...(event.dataTransfer?.items ?? [])] .filter(({ kind }) => kind === "file") .map(itemToFiles))).flat(); } // Unsupported event, no files. return []; }; let itemToFiles = async (item) => { // Note: `webkitGetAsEntry` may not be available in all browsers. let entry = item.webkitGetAsEntry?.(); // Note: `getAsFile` returns a non-null `File` even for directories. let file = item.getAsFile(); // No entry or file for this item. if (!entry && !file) return []; // A file exists but not its entry; it could be a real file or a directory. if (!entry && file) return [{ file }]; // A true file exists, return it with its entry. if (entry?.isFile && file) return [{ file, entry: entry }]; // Try to get the files from the entry. return await entryToFiles(entry); }; let entryToFiles = async (entry) => { // Entry is a single file. if (entry?.isFile) { return [await fileEntryToFile(entry)]; } // Entry is a directory. if (entry?.isDirectory) { return await dirEntryToFiles(entry); } // Unknown or null entry, no files. return []; }; let fileEntryToFile = (entry) => { return new Promise((resolve, reject) => { entry.file((file) => { resolve({ file, entry }); }, reject); }); }; let dirEntryToFiles = async (dirEntry) => { let files = []; // Loop until we read all directories. let dirEntries = [dirEntry]; while (dirEntries.length) { // Loop until we read all entries in the current directory. let reader = dirEntries.pop().createReader(); while (true) { // Read some entries from the directory. let entries = await new Promise((resolve, reject) => { reader.readEntries(resolve, reject); }); // No more entries in this directory. if (!entries.length) break; // Handle found entries. for (let entry of entries) { if (entry.isFile) { files.push(await fileEntryToFile(entry)); } else if (entry.isDirectory) { dirEntries.push(entry); } } } } // Return directory files. return files; };