11/04/2024
Forestil dig en verden, hvor dine webapplikationer kan interagere problemfrit med dine lokale filer og mapper, præcis som desktop-programmer. Lyder det som fremtidens musik? Med JavaScripts Fil- og Mappeindgangs-API'en (File and Directory Entries API) er denne fremtid allerede her. Denne kraftfulde API revolutionerer den måde, vi interagerer med data på nettet, ved at give udviklere mulighed for at skabe rige, filbaserede oplevelser direkte i browseren, uden at brugeren skal forlade websiden.

Traditionelt har håndtering af filer på en brugers maskine fra en webapplikation været en manuel og ofte besværlig proces, typisk begrænset til upload og download. Men takket være Fil- og Mappeindgangs-API'en kan udviklere nu tilbyde en intuitiv grænseflade, der gør det muligt at læse, skrive, organisere og slette filer og mapper direkte fra en webapplikation. Dette åbner op for et væld af nye muligheder for applikationer, der kræver dybere integration med det lokale filsystem, såsom offline-redigeringsværktøjer, mediehåndtering eller lokale backup-løsninger.
En af de mest afgørende funktioner ved denne API er dens asynkrone natur. Dette betyder, at API-kaldene ikke blokerer browserens hovedtråd, hvilket sikrer en glat og responsiv brugeroplevelse, selv når der håndteres store mængder data eller komplekse filoperationer. For mobile enheder, hvor ressourcer ofte er mere begrænsede, er denne asynkrone adfærd særligt vigtig for at opretholde en hurtig og flydende interaktion.
- Sådan Anmodes om Adgang til Filsystemet
- Læsning af Filer og Mapper
- Oprettelse af Filer og Mapper
- Skrivning til Filer
- Sletning af Filer og Mapper
- Åbning af Mapper: Den Moderne vs. Klassiske Tilgang
- Sikkerhed og Begrænsninger: Hvorfor Du Ikke Kan Åbne en Mappe Direkte i Windows
- Fremtiden for Filhåndtering i Browseren
- Ofte Stillede Spørgsmål (FAQ)
Sådan Anmodes om Adgang til Filsystemet
Før en webapplikation kan begynde at interagere med brugerens lokale filsystem, skal den anmode om tilladelse. Dette er en afgørende sikkerhedsforanstaltning, der sikrer, at brugeren altid har kontrol over, hvilke mapper en webapplikation kan tilgå. Den primære metode til dette er window.showDirectoryPicker() funktionen, som beder brugeren om at vælge en mappe.
Når denne funktion kaldes, vil browseren vise en systemdialog, hvor brugeren kan navigere og vælge den ønskede mappe. Brugeren skal eksplicit give samtykke, før applikationen får adgang. Denne proces er designet til at være transparent og sikker, så brugerne kan føle sig trygge ved at give adgang til deres data.
async function getDirectoryHandle() {
try {
const directoryHandle = await window.showDirectoryPicker();
console.log('Mappehåndtag modtaget:', directoryHandle.name);
return directoryHandle;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Brugeren annullerede mappevalget.');
} else {
console.error('Fejl ved anmodning om mappeadgang:', error);
}
return null;
}
}Når en mappe er valgt og godkendt, returnerer funktionen et directoryHandle-objekt. Dette håndtag er en sikker reference til den valgte mappe og kan bruges til yderligere operationer som at læse, skrive eller slette filer og undermapper. Det er vigtigt at bemærke, at adgangen ofte er sessionsbaseret, hvilket betyder, at brugeren muligvis skal give tilladelse igen, hvis siden genindlæses, selvom nogle browsere tilbyder mulighed for persistent adgang efter første godkendelse.
Læsning af Filer og Mapper
Når du har et mappehåndtag, kan du iterere gennem dets indhold ved hjælp af for await...of løkken. Dette er en elegant måde at håndtere den asynkrone natur af filsystemoperationer på. Du kan nemt skelne mellem filer og mapper og håndtere dem i overensstemmelse hermed.
async function listContents(directoryHandle, indent = 0) {
const prefix = ' '.repeat(indent);
for await (const entry of directoryHandle.values()) {
console.log(${prefix}${entry.kind}: ${entry.name});
if (entry.kind === 'directory') {
await listContents(entry, indent + 1); // Rekursivt kald for indlejrede mapper
} else if (entry.kind === 'file') {
// Eksempel: Læs indholdet af en tekstfil
if (entry.name.endsWith('.txt')) {
try {
const file = await entry.getFile();
const text = await file.text();
console.log(${prefix} Indhold af ${entry.name}: ${text.substring(0, 50)}...);
} catch (readError) {
console.error(${prefix} Fejl ved læsning af fil ${entry.name}:, readError);
}
}
}
}
}Denne funktion logger typen (fil eller mappe) og navnet på hver post til konsollen. For mapper dykker den dybere rekursivt, hvilket giver dig mulighed for at udforske undermapper. Dette er ideelt til at bygge filbrowsere, fotoalbum eller andre applikationer, der skal vise en organiseret oversigt over brugerens lokale data.
Oprettelse af Filer og Mapper
Efter at have fået adgang til en mappe, er det ofte nødvendigt at oprette nye filer eller undermapper inden i den. Dette er ligetil med API'en, ved at bruge getFileHandle() og getDirectoryHandle() metoderne med et ekstra parameter.

async function createFile(directoryHandle, fileName, initialContents = '') {
try {
const newFileHandle = await directoryHandle.getFileHandle(fileName, { create: true });
console.log('Fil oprettet:', newFileHandle.name);
if (initialContents) {
await writeFile(newFileHandle, initialContents);
}
return newFileHandle;
} catch (error) {
console.error('Fejl ved oprettelse af fil:', error);
return null;
}
}
async function createDirectory(directoryHandle, directoryName) {
try {
const newDirHandle = await directoryHandle.getDirectoryHandle(directoryName, { create: true });
console.log('Mappe oprettet:', newDirHandle.name);
return newDirHandle;
} catch (error) {
console.error('Fejl ved oprettelse af mappe:', error);
return null;
}
}Nøglen her er det andet parameter { create: true }. Dette sikrer, at den specificerede fil eller mappe oprettes, hvis den ikke allerede eksisterer. Hvis den eksisterer, returneres det eksisterende håndtag, hvilket gør disse funktioner både til oprettelse og tilgang.
Skrivning til Filer
For at skrive data til en fil giver API'en en skrivbar strøm (writable stream). Denne strøm-baserede tilgang er yderst effektiv, især for store filer, da den ikke kræver, at hele filen indlæses i hukommelsen på én gang.
async function writeFile(fileHandle, contents) {
try {
const writable = await fileHandle.createWritable();
await writable.write(contents);
await writable.close();
console.log('Fil skrevet succesfuldt.');
} catch (error) {
console.error('Fejl ved skrivning til fil:', error);
}
}Denne funktion accepterer et filhåndtag og indholdet (som kan være en streng, Blob eller BufferSource), du ønsker at skrive. Den opretter en skrivbar strøm, skriver dataene og lukker derefter strømmen for at gemme ændringerne. Dette gør det muligt at bygge robuste lokale redaktører eller dataeksportfunktioner.
Sletning af Filer og Mapper
Sletning af filer og mapper kan udføres ved at kalde removeEntry() på et mappehåndtag, hvor du angiver navnet på den fil eller mappe, du ønsker at slette. Det er vigtigt at udvise forsigtighed med denne operation, da den permanent fjerner data.
async function deleteEntry(directoryHandle, entryName) {
try {
await directoryHandle.removeEntry(entryName);
console.log('Post slettet:', entryName);
} catch (error) {
console.error('Fejl ved sletning af post:', error);
}
}Med disse grundlæggende operationer kan du strukturere din egen applikation til at uploade, læse, skrive og slette filsystemposter i henhold til forretnings- eller brugerbehov. Evnen til at administrere og organisere filstrukturer via browseren er en kraftfuld funktion, der kan føre til mere dynamiske og brugervenlige webapplikationer.
Åbning af Mapper: Den Moderne vs. Klassiske Tilgang
Spørgsmålet om, hvordan man åbner en mappe direkte fra en webapplikation, er et hyppigt diskuteret emne. Mens den moderne Fil- og Mappeindgangs-API tilbyder en robust løsning, findes der også ældre, mere begrænsede metoder. Det er vigtigt at forstå forskellene og vide, hvornår man skal anvende hvilken metode.
Den Moderne Tilgang: showDirectoryPicker()
Som tidligere nævnt er window.showDirectoryPicker() den anbefalede og mest moderne måde at anmode om adgang til en mappe på. Denne metode giver ikke kun læseadgang, men kan også anmode om skriveadgang ved at passere { mode: 'readwrite' } som et parameter. Dette er afgørende for webapplikationer, der skal kunne modificere indholdet i den valgte mappe.
Denne tilgang er understøttet i nyere versioner af Chrome, Edge og Opera (version 86 og opefter). Den giver en strømlinet brugeroplevelse og sikrer, at udviklere får fulde FileSystemDirectoryHandle og FileSystemFileHandle objekter, som er nødvendige for de mere avancerede filoperationer.

Den Klassiske Tilgang: <input type="file" webkitdirectory>
Før Fil- og Mappeindgangs-API'en blev bredt tilgængelig, var den mest almindelige måde at lade brugere vælge en mappe på via <input type="file" webkitdirectory> elementet. Selvom navnet webkitdirectory antyder en specifik browsermotor, er denne attribut blevet bredt understøttet i de fleste moderne browsere i mange år.
Denne metode giver brugeren mulighed for at vælge en mappe, men den returnerer kun en liste over filer inden for den valgte mappe og dens undermapper. Den giver ikke direkte adgang til mappehåndtag eller mulighed for at skrive til filsystemet. Det er primært en læse-kun-løsning til masse-upload af filer fra en mappe.
For at bruge denne tilgang programmatisk, opretter man typisk et usynligt <input> element i JavaScript og "klikker" på det:
const openDirectoryClassic = () => {
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);
});
// Forsøg at vise filvælgeren direkte, ellers simuler et klik
if ('showPicker' in HTMLInputElement.prototype) {
input.showPicker();
} else {
input.click();
}
});
};Denne "klassiske" metode er en god fallback for browsere, der ikke understøtter showDirectoryPicker(), men den er begrænset i dens funktionalitet.
Progressiv Forbedring: Det Bedste fra Begge Verdener
Den bedste praksis er at anvende progressiv forbedring. Dette betyder, at du bruger den mest avancerede API (showDirectoryPicker()) når den er tilgængelig, og falder tilbage til den ældre metode (<input type="file" webkitdirectory>) hvis den moderne API ikke understøttes. Dette sikrer den bedste brugeroplevelse for flest mulige brugere.
Følgende kodeeksempel demonstrerer denne tilgang, som også vist af Thomas Steiner:
const openDirectory = async (mode = "read") => {
// Feature detection. API'en skal understøttes
// og app'en må ikke køre i en iframe for fuld funktionalitet.
const supportsFileSystemAccess = "showDirectoryPicker" in window && (() => {
try {
return window.self === window.top; // Sikrer at den ikke kører i en iframe
} catch {
return false;
}
})();
// Hvis File System Access API'en understøttes…
if (supportsFileSystemAccess) {
let directoryStructure = undefined;
// Rekursiv funktion der gennemgår mappestrukturen.
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 {
// Åbn mappen.
const handle = await showDirectoryPicker({ mode, });
// Hent mappestrukturen.
directoryStructure = getFiles(handle, undefined);
} catch (err) {
if (err.name !== "AbortError") {
console.error(err.name, err.message);
}
}
return directoryStructure;
}
// Fallback hvis File System Access API'en ikke understøttes.
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();
}
});
};I dette eksempel returnerer funktionen i begge tilfælde en liste over filer, men når File System Access API'en understøttes, vil hvert filobjekt også have et FileSystemDirectoryHandle gemt i directoryHandle-egenskaben og et FileSystemFileHandle i handle-egenskaben. Dette giver udviklere adgang til de mere avancerede funktioner, når de er tilgængelige.
Sikkerhed og Begrænsninger: Hvorfor Du Ikke Kan Åbne en Mappe Direkte i Windows
Et ofte stillet spørgsmål er, hvorfor en webapplikation ikke direkte kan åbne en mappe i operativsystemet (f.eks. Windows Stifinder) fra en HTML-side. Svaret er simpelt: sikkerhed. Browsere implementerer strenge sikkerhedsrestriktioner for at forhindre ondsindede websteder i at få ukontrolleret adgang til eller manipulere brugerens lokale maskine. Hvis en hjemmeside kunne udføre arbitrære kommandoer på dit operativsystem, ville det udgøre en enorm sikkerhedsrisiko.
Dette er grunden til, at webapplikationer ikke kan starte eksterne programmer eller direkte navigere til lokale filstier som file:///D:/ uden for browserens sandkasse. Den Fil- og Mappeindgangs-API'en opererer stadig inden for browserens sikkerhedsrammer, hvilket betyder, at den kræver eksplicit brugersamtykke for hver operation, og den giver ikke mulighed for at "springe ud" af browseren for at åbne en systemmappe.
Løsningen: Downloadbare Genveje (URL-filer)
Selvom direkte åbning er blokeret, findes der en omvej for specifikke scenarier, især på Windows: at levere et downloadbart link til en .URL-fil. Disse filer er tekstbaserede genveje, der, når de åbnes af operativsystemet, dirigerer brugeren til en angivet URL eller filsti.

En .URL-fil er en simpel tekstfil med et specifikt format:
[InternetShortcut]
URL=file:///D:/MinMappe/Når en bruger downloader og åbner denne .URL-fil, vil operativsystemet forsøge at åbne den specificerede sti. Dette kræver dog brugerinteraktion: filen skal downloades og derefter åbnes manuelt. Dette er ikke en direkte browserbaseret løsning, men en metode til at "overlevere" en instruktion til operativsystemet.
For at denne løsning skal fungere, skal din webserver konfigureres til at servere .URL-filer korrekt, ofte ved at tilføje application/internet-shortcut som MIME-type. Browsere som Chrome og Firefox vil typisk downloade filen først, hvorefter brugeren skal klikke på den for at åbne den. Internet Explorer havde tidligere en mere direkte "Åbn"-mulighed, men moderne browsere prioriterer sikkerhed via download-først-modellen.
Det er vigtigt at understrege, at denne .URL-filmetode primært er relevant for Windows og ikke er en del af Fil- og Mappeindgangs-API'en. Den løser et specifikt problem for desktop-brugere, men er ikke en generel løsning for alle operativsystemer eller mobilbrowsere.
Fremtiden for Filhåndtering i Browseren
Fil- og Mappeindgangs-API'en repræsenterer et stort skridt fremad for webudvikling. Den muliggør oprettelsen af webapplikationer, der føles mere som native desktop-apps, især inden for områder som:
- Offline-redigeringsværktøjer: Tekstbehandlingsprogrammer, billedredigerere eller CAD-værktøjer, der kan gemme og indlæse filer lokalt.
- Mediehåndtering: Lokale fotoalbum, videoafspillere eller musikbiblioteker, der kan organisere og afspille medier direkte fra brugerens harddisk.
- Data-analyse og -visualisering: Applikationer, der kan behandle store datasæt lokalt uden at skulle uploade dem til en server.
- Lokale backup- og synkroniseringsværktøjer: Applikationer, der kan hjælpe brugere med at administrere deres lokale data.
Selvom API'en stadig er under udvikling og browserunderstøttelsen primært er koncentreret om Chromium-baserede browsere, er potentialet enormt. Den fortsatte udvikling og bredere adoption vil uden tvivl transformere, hvad vi forventer af webapplikationer.
Ofte Stillede Spørgsmål (FAQ)
Hvad er den primære fordel ved Fil- og Mappeindgangs-API'en?
Den primære fordel er, at webapplikationer kan interagere direkte med det lokale filsystem for at læse, skrive og organisere filer og mapper. Dette giver en meget rigere brugeroplevelse og muliggør avancerede offline-funktioner, som tidligere kun var mulige med desktop-applikationer.
Er Fil- og Mappeindgangs-API'en sikker at bruge?
Ja, API'en er designet med sikkerhed i tankerne. Den kræver altid eksplicit brugersamtykke for at få adgang til en mappe, og brugeren kan til enhver tid trække denne tilladelse tilbage. Webapplikationen opererer stadig inden for browserens sandkasse, hvilket forhindrer direkte adgang til systemressourcer eller vilkårlig filmanipulation.
Kan jeg bruge denne API på alle browsere, inklusive mobilbrowsere?
I skrivende stund er Fil- og Mappeindgangs-API'en primært understøttet i Chromium-baserede browsere som Google Chrome, Microsoft Edge og Opera. Understøttelse på mobilbrowsere (f.eks. Chrome på Android) er også til stede. Andre browsere som Firefox og Safari har ikke fuld understøttelse, men arbejder på lignende funktionalitet. Det er derfor vigtigt at bruge progressiv forbedring og tilbyde en fallback-løsning for bred kompatibilitet.
Hvad er forskellen på showDirectoryPicker() og <input type="file" webkitdirectory>?
showDirectoryPicker() er den moderne API, der giver en webapplikation et sikkert håndtag til en valgt mappe, hvilket muliggør både læse- og skriveadgang samt navigation i mappestrukturen. <input type="file" webkitdirectory> er en ældre HTML-attribut, der kun tillader valg af en mappe for at uploade alle dens filer (og undermappers filer) til applikationen. Den giver kun læseadgang og ingen direkte mappehåndtag til fortsat interaktion.
Hvis du vil læse andre artikler, der ligner Revolutionerende Filstyring i Din Browser, kan du besøge kategorien Teknologi.
