Как открыть каталог

Работа с каталогами — это не то, с чем вы будете сталкиваться ежедневно, но иногда возникают ситуации, например, когда требуется обработать все изображения в каталоге. Благодаря API доступа к файловой системе пользователи теперь могут открывать каталоги в браузере и решать, нужен ли им доступ на запись или нет.

Использование метода showDirectoryPicker() API доступа к файловой системе.

Чтобы открыть каталог, вызовите showDirectoryPicker() , который возвращает обещание с выбранным каталогом. Если вам нужен доступ на запись, вы можете передать методу { mode: 'readwrite' } .

Поддержка браузера

  • 86
  • 86
  • Икс
  • Икс

Источник

Классический способ

Использование элемента <input type="file" webkitdirectory>

Элемент <input type="file" webkitdirectory> на странице позволяет пользователю щелкнуть его и открыть каталог. Хитрость теперь состоит в том, чтобы незаметно вставить элемент на страницу с помощью JavaScript и программно щелкнуть по нему.

Поддержка браузера

  • 7
  • 13
  • 50
  • 11.1

Источник

Прогрессивное улучшение

Приведенный ниже метод использует API доступа к файловой системе, если он поддерживается, а в противном случае возвращается к классическому подходу. В обоих случаях функция возвращает каталог, но в случае поддержки API доступа к файловой системе каждый файловый объект также имеет FileSystemDirectoryHandle хранящийся в свойстве directoryHandle , и FileSystemFileHandle хранящийся в свойстве handle , поэтому вы можете дополнительно сериализовать дескрипторы. на диск.

const openDirectory = async (mode = "read") => {
 
// Feature detection. The API needs to be supported
 
// and the app not run in an iframe.
 
const supportsFileSystemAccess =
   
"showDirectoryPicker" in window &&
   
(() => {
     
try {
       
return window.self === window.top;
     
} catch {
       
return false;
     
}
   
})();
 
// If the File System Access API is supported…
 
if (supportsFileSystemAccess) {
    let directoryStructure
= undefined;

   
// Recursive function that walks the directory structure.
   
const getFiles = async (dirHandle, path = dirHandle.name) => {
     
const dirs = [];
     
const files = [];
     
for await (const entry of dirHandle.values()) {
       
const nestedPath = `${path}/${entry.name}`;
       
if (entry.kind === "file") {
          files
.push(
            entry
.getFile().then((file) => {
              file
.directoryHandle = dirHandle;
              file
.handle = entry;
             
return Object.defineProperty(file, "webkitRelativePath", {
                configurable
: true,
                enumerable
: true,
               
get: () => nestedPath,
             
});
           
})
         
);
       
} else if (entry.kind === "directory") {
          dirs
.push(getFiles(entry, nestedPath));
       
}
     
}
     
return [
       
...(await Promise.all(dirs)).flat(),
       
...(await Promise.all(files)),
     
];
   
};

   
try {
     
// Open the directory.
     
const handle = await showDirectoryPicker({
        mode
,
     
});
     
// Get the directory structure.
      directoryStructure
= getFiles(handle, undefined);
   
} catch (err) {
     
if (err.name !== "AbortError") {
        console
.error(err.name, err.message);
     
}
   
}
   
return directoryStructure;
 
}
 
// Fallback if the File System Access API is not supported.
 
return new Promise((resolve) => {
   
const input = document.createElement('input');
    input
.type = 'file';
    input
.webkitdirectory = true;

    input
.addEventListener('change', () => {
      let files
= Array.from(input.files);
      resolve
(files);
   
});
   
if ('showPicker' in HTMLInputElement.prototype) {
      input
.showPicker();
   
} else {
      input
.click();
   
}
 
});
};

дальнейшее чтение

Демо

<!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 a directory</title>
 
</head>
 
<body>
   
<h1>How to open a directory</h1>
   
<button type="button">Open directory</button>
   
<pre></pre>
 
</body>
</html>

       
:root {
 
color-scheme: dark light;
}

html
{
 
box-sizing: border-box;
}

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

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

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

const openDirectory = async (mode = "read") => {
 
// Feature detection. The API needs to be supported
 
// and the app not run in an iframe.
 
const supportsFileSystemAccess =
   
"showDirectoryPicker" in window &&
   
(() => {
     
try {
       
return window.self === window.top;
     
} catch {
       
return false;
     
}
   
})();
 
// If the File System Access API is supported…
 
if (supportsFileSystemAccess) {
    let directoryStructure
= undefined;

   
const getFiles = async (dirHandle, path = dirHandle.name) => {
     
const dirs = [];
     
const files = [];
     
for await (const entry of dirHandle.values()) {
       
const nestedPath = `${path}/${entry.name}`;
       
if (entry.kind === "file") {
          files
.push(
            entry
.getFile().then((file) => {
              file
.directoryHandle = dirHandle;
              file
.handle = entry;
             
return Object.defineProperty(file, "webkitRelativePath", {
                configurable
: true,
                enumerable
: true,
               
get: () => nestedPath,
             
});
           
})
         
);
       
} else if (entry.kind === "directory") {
          dirs
.push(getFiles(entry, nestedPath));
       
}
     
}
     
return [
       
...(await Promise.all(dirs)).flat(),
       
...(await Promise.all(files)),
     
];
   
};

   
try {
     
const handle = await showDirectoryPicker({
        mode
,
     
});
      directoryStructure
= getFiles(handle, undefined);
   
} catch (err) {
     
if (err.name !== "AbortError") {
        console
.error(err.name, err.message);
     
}
   
}
   
return directoryStructure;
 
}
 
// Fallback if the File System Access API is not supported.
 
return new Promise((resolve) => {
   
const input = document.createElement('input');
    input
.type = 'file';
    input
.webkitdirectory = true;

    input
.addEventListener('change', () => {
      let files
= Array.from(input.files);
      resolve
(files);
   
});
   
if ('showPicker' in HTMLInputElement.prototype) {
      input
.showPicker();
   
} else {
      input
.click();
   
}
 
});
};

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