Bir veya daha fazla dosyayı açma

Thomas Steiner'ın yer aldığı daha fazla içerik
Thomas Steiner

Dosyalarla ilgilenme, web'deki uygulamaların en yaygın işlemlerinden biridir. Geleneksel olarak, kullanıcıların bir dosya yüklemesi, bu dosyada bazı değişiklikler yapması, ardından dosyayı tekrar indirmesi ve bunun sonucunda dosyanın İndirilenler klasörüne kopyalanması gerekiyordu. File System Access API ile kullanıcılar artık dosyaları doğrudan açabilir, değişiklikler yapabilir ve değişiklikleri orijinal dosyaya kaydedebilir.

Modern yöntem

File System Access API'nin showOpenFilePicker() yöntemini kullanma

Bir dosyayı açmak için showOpenFilePicker() çağrısı yapın. Bu çağrı, seçilen dosya veya dosyalardan oluşan bir diziyle bir söz döndürür. Birden fazla dosyaya ihtiyacınız varsa { multiple: true, } öğesini söz konusu yönteme aktarabilirsiniz.

Tarayıcı Desteği

  • 86
  • 86
  • x
  • x

Kaynak

Klasik yöntem

<input type="file"> öğesini kullanma

Sayfadaki <input type="file"> öğesi, kullanıcının öğeyi tıklayıp bir veya daha fazla dosyayı açmasına olanak tanır. Buradaki ipucu, öğeyi JavaScript içeren bir sayfaya görünmez bir şekilde eklemek ve programlı bir şekilde tıklamaktır.

Tarayıcı Desteği

  • 1
  • 12
  • 1
  • 1

Kaynak

Progresif geliştirme

Aşağıdaki yöntem, desteklendiğinde File System Access API'yi kullanır, aksi takdirde klasik yaklaşıma döner. İşlev her iki durumda da bir dosya dizisi döndürür ancak File System Access API'nin desteklendiği durumlarda her dosya nesnesinin handle özelliğinde depolanan bir FileSystemFileHandle özelliği de vardır. Böylece, herkese açık kullanıcı adını isteğe bağlı olarak diske serileyebilirsiniz.

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();
    }
  });
};

Daha fazla bilgi

Demo

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`));
});