Jak otworzyć jeden lub wiele plików

Obsługa plików to jedna z najczęstszych czynności w aplikacjach w internecie. Tradycyjnie użytkownicy musieli przesłać plik, wprowadzić w nim pewne zmiany, a następnie pobrać go ponownie, co spowodowało utworzenie kopii w folderze Pobrane pliki. Dzięki interfejsowi File System Access API użytkownicy mogą teraz bezpośrednio otwierać pliki, wprowadzać zmiany i zapisywać zmiany w pierwotnym pliku.

Nowoczesny sposób

Za pomocą metody showOpenFilePicker() interfejsu File System Access API

Aby otworzyć plik, wywołaj metodę showOpenFilePicker(), która zwraca obietnicę z tablicą wybranego pliku lub plików. Jeśli potrzebujesz wielu plików, możesz przekazać metodę { multiple: true, }.

Obsługa przeglądarek

  • 86
  • 86
  • x
  • x

Źródło

Klasyczny sposób

Korzystanie z elementu <input type="file">

Element <input type="file"> na stronie pozwala użytkownikowi kliknąć go i otworzyć jeden lub wiele plików. Teraz trzeba w niewidoczny sposób wstawić element na stronie za pomocą kodu JavaScript i kliknąć go w sposób zautomatyzowany.

Obsługa przeglądarek

  • 1
  • 12
  • 1
  • 1

Źródło

Stopniowe ulepszanie

Poniższa metoda korzysta z interfejsu File System Access API, gdy jest obsługiwany, lub wykorzystuje podejście klasyczne. W obu przypadkach funkcja zwraca tablicę plików, ale jeśli interfejs File System Access API jest obsługiwany, każdy obiekt file ma również element FileSystemFileHandle zapisany we właściwości handle, dzięki czemu możesz opcjonalnie serializować nick na dysku.

const openFileOrFiles = async (multiple = false) => {
  // Feature detection. The API needs to be supported
  // and the app not run in an iframe.
  const supportsFileSystemAccess =
    "showOpenFilePicker" in window &&
    (() => {
      try {
        return window.self === window.top;
      } catch {
        return false;
      }
    })();
  // If the File System Access API is supported…
  if (supportsFileSystemAccess) {
    let fileOrFiles = undefined;
    try {
      // Show the file picker, optionally allowing multiple files.
      const handles = await showOpenFilePicker({ multiple });
      // Only one file is requested.
      if (!multiple) {
        // Add the `FileSystemFileHandle` as `.handle`.
        fileOrFiles = await handles[0].getFile();
        fileOrFiles.handle = handles[0];
      } else {
        fileOrFiles = await Promise.all(
          handles.map(async (handle) => {
            const file = await handle.getFile();
            // Add the `FileSystemFileHandle` as `.handle`.
            file.handle = handle;
            return file;
          })
        );
      }
    } catch (err) {
      // Fail silently if the user has simply canceled the dialog.
      if (err.name !== 'AbortError') {
        console.error(err.name, err.message);
      }
    }
    return fileOrFiles;
  }
  // Fallback if the File System Access API is not supported.
  return new Promise((resolve) => {
    // Append a new `<input type="file" multiple? />` and hide it.
    const input = document.createElement('input');
    input.style.display = 'none';
    input.type = 'file';
    document.body.append(input);
    if (multiple) {
      input.multiple = true;
    }
    // The `change` event fires when the user interacts with the dialog.
    input.addEventListener('change', () => {
      // Remove the `<input type="file" multiple? />` again from the DOM.
      input.remove();
      // If no files were selected, return.
      if (!input.files) {
        return;
      }
      // Return all files or just one file.
      resolve(multiple ? Array.from(input.files) : input.files[0]);
    });
    // Show the picker.
    if ('showPicker' in HTMLInputElement.prototype) {
      input.showPicker();
    } else {
      input.click();
    }
  });
};

Więcej informacji

Prezentacja

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="icon"
      href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📂</text></svg>"
    />
    <title>How to open one or multiple files</title>
  </head>
  <body>
    <h1>How to open one or multiple files</h1>
    <button type="button">Open file</button>
    <button class="multiple" type="button">Open files</button>
    <pre></pre>
  </body>
</html>

CSS


        :root {
  color-scheme: dark light;
}

html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin: 1rem;
  font-family: system-ui, sans-serif;
}

button {
  margin: 1rem;
}
        

JS


        const button = document.querySelector('button');
const buttonMultiple = document.querySelector('button.multiple');
const pre = document.querySelector('pre');

const openFileOrFiles = async (multiple = false) => {
  // Feature detection. The API needs to be supported
  // and the app not run in an iframe.
  const supportsFileSystemAccess =
    "showOpenFilePicker" in window &&
    (() => {
      try {
        return window.self === window.top;
      } catch {
        return false;
      }
    })();

  // If the File System Access API is supported…
  if (supportsFileSystemAccess) {
    let fileOrFiles = undefined;
    try {
      // Show the file picker, optionally allowing multiple files.
      fileOrFiles = await showOpenFilePicker({ multiple });
      if (!multiple) {
        // Only one file is requested.
        fileOrFiles = fileOrFiles[0];
      }
    } catch (err) {
      // Fail silently if the user has simply canceled the dialog.
      if (err.name !== 'AbortError') {
        console.error(err.name, err.message);
      }
    }
    return fileOrFiles;
  }
  // Fallback if the File System Access API is not supported.
  return new Promise((resolve) => {
    // Append a new `` and hide it.
    const input = document.createElement('input');
    input.style.display = 'none';
    input.type = 'file';
    document.body.append(input);
    if (multiple) {
      input.multiple = true;
    }
    // The `change` event fires when the user interacts with the dialog.
    input.addEventListener('change', () => {
      // Remove the `` again from the DOM.
      input.remove();
      // If no files were selected, return.
      if (!input.files) {
        return;
      }
      // Return all files or just one file.
      resolve(multiple ? input.files : input.files[0]);
    });
    // Show the picker.
    if ('showPicker' in HTMLInputElement.prototype) {
      input.showPicker();
    } else {
      input.click();
    }
  });
};

button.addEventListener('click', async () => {
  const file = await openFileOrFiles();
  if (!file) {
    return;
  }
  pre.textContent += `${file.name}\n`;
});

buttonMultiple.addEventListener('click', async () => {
  const files = await openFileOrFiles(true);
  if (!files) {
    return;
  }
  Array.from(files).forEach((file) => (pre.textContent += `${file.name}\n`));
});