Skip to content

Commit b678860

Browse files
Add File System API examples (#323)
* Add File System API examples * Update file-system-api/createsyncaccesshandle-mode/index.html Co-authored-by: Dipika Bhattacharya <dipika@foss-community.org> * Update file-system-api/createsyncaccesshandle-mode/index.html Co-authored-by: Dipika Bhattacharya <dipika@foss-community.org> * Update file-system-api/createwritable-mode/index.html Co-authored-by: Dipika Bhattacharya <dipika@foss-community.org> * Update file-system-api/createwritable-mode/index.html Co-authored-by: Dipika Bhattacharya <dipika@foss-community.org> --------- Co-authored-by: Dipika Bhattacharya <dipika@foss-community.org>
1 parent 6f196ec commit b678860

File tree

11 files changed

+365
-1
lines changed

11 files changed

+365
-1
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ Code examples that accompany various MDN DOM and Web API documentation pages.
3535

3636
- The "edit-context" directory contains examples demonstrating the [EditContext API](https://developer.mozilla.org/docs/Web/API/EditContext_API). See the [list of examples](https://github.com/mdn/dom-examples/tree/main/edit-context/).
3737

38-
- The "filesystemobserver" directory contains an example demonstrating usage of the [FileSystemObserver](https://developer.mozilla.org/docs/Web/API/FileSystemObserver) API ([run the example live](https://mdn.github.io/dom-examples/filesystemobserver/)). This example was originally published on Glitch by [Thomas Steiner](https://front-end.social/@tomayac@toot.cafe).
38+
- The "file-system-api" directory contains multiple examples demonstrating usage of the [File System API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API):
39+
40+
- [createSyncAccessHandle() mode test](https://mdn.github.io/dom-examples/file-system-api/createsyncaccesshandle-mode/): Demonstrates usage of [FileSystemFileHandle.createSyncAccessHandle()](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle)
41+
- [createWritable() mode test](https://mdn.github.io/dom-examples/file-system-api/createwritable-mode/): Demonstrates usage of [FileSystemFileHandle.createWritable()](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createWritable)
42+
- [FileSystemHandle.remove() demo](https://mdn.github.io/dom-examples/file-system-api/filesystemhandle-remove/): Demonstrates usage of [FileSystemHandle.remove()](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle/remove)
43+
- [File System Observer Demo](https://mdn.github.io/dom-examples/file-system-api/filesystemobserver/): Demonstrates usage of the [FileSystemObserver](https://developer.mozilla.org/docs/Web/API/FileSystemObserver) API. This example was originally published on Glitch by [Thomas Steiner](https://front-end.social/@tomayac@toot.cafe).
3944

4045
- The "fullscreen-api" directory is for examples and demos of the [Fullscreen API](https://wiki.developer.mozilla.org/docs/Web/API/Fullscreen_API). Run the [example live](https://mdn.github.io/dom-examples/fullscreen-api/).
4146

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>createSyncAccessHandle() mode test</title>
6+
<style>
7+
li {
8+
margin-bottom: 10px;
9+
}
10+
</style>
11+
</head>
12+
13+
<body>
14+
<h1>createSyncAccessHandle() mode test</h1>
15+
<ol>
16+
<li>
17+
<label for="filetext">Enter text to write to the file:</label>
18+
<input type="text" id="filetext" name="filetext" />
19+
</li>
20+
<li>
21+
Write your text to the file: <button class="write">Write text</button>
22+
</li>
23+
<li>
24+
Empty the file if it gets too full:
25+
<button class="empty">Empty file</button>
26+
</li>
27+
</ol>
28+
<footer>
29+
<p>
30+
Check out the
31+
<a
32+
href="https://github.com/mdn/dom-examples/tree/main/file-system-api/createsyncaccesshandle-mode"
33+
>source code</a
34+
>.
35+
</p>
36+
</footer>
37+
<script>
38+
const writeBtn = document.querySelector(".write");
39+
const emptyBtn = document.querySelector(".empty");
40+
const fileText = document.querySelector("#filetext");
41+
42+
const opfsWorker = new Worker("worker.js");
43+
44+
function writeToOPFS() {
45+
opfsWorker.postMessage({
46+
command: "write",
47+
content: fileText.value,
48+
});
49+
console.log("Main script: Text posted to worker");
50+
fileText.value = "";
51+
}
52+
53+
function emptyOPFS() {
54+
opfsWorker.postMessage({
55+
command: "empty",
56+
});
57+
}
58+
59+
writeBtn.addEventListener("click", writeToOPFS);
60+
emptyBtn.addEventListener("click", emptyOPFS);
61+
</script>
62+
</body>
63+
</html>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
let accessHandle;
2+
3+
async function initOPFS() {
4+
const opfsRoot = await navigator.storage.getDirectory();
5+
const fileHandle = await opfsRoot.getFileHandle("file.txt", { create: true });
6+
accessHandle = await fileHandle.createSyncAccessHandle({
7+
mode: "readwrite-unsafe",
8+
});
9+
}
10+
11+
initOPFS();
12+
13+
onmessage = function (e) {
14+
console.log("Worker: Message received from main script");
15+
16+
// Get the current size of the file
17+
let size = accessHandle.getSize();
18+
19+
if (e.data.command === "empty") {
20+
// Truncate the file to 0 bytes
21+
accessHandle.truncate(0);
22+
23+
// Get the current size of the file
24+
size = accessHandle.getSize();
25+
} else {
26+
const textEncoder = new TextEncoder();
27+
const textDecoder = new TextDecoder();
28+
29+
// Encode content to write to the file
30+
const content = textEncoder.encode(e.data.content);
31+
// Write the content at the end of the file
32+
accessHandle.write(content, { at: size });
33+
34+
// Get the current size of the file
35+
size = accessHandle.getSize();
36+
37+
// Prepare a data view of the length of the file
38+
const dataView = new DataView(new ArrayBuffer(size));
39+
40+
// Read the entire file into the data view
41+
accessHandle.read(dataView, { at: 0 });
42+
43+
// Log the current file contents to the console
44+
console.log("File contents: " + textDecoder.decode(dataView));
45+
46+
// Flush the changes
47+
accessHandle.flush();
48+
}
49+
50+
// Log the size of the file to the console
51+
console.log("Size: " + size);
52+
};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>createWritable() mode test</title>
6+
<style>
7+
li {
8+
margin-bottom: 10px;
9+
}
10+
</style>
11+
</head>
12+
13+
<body>
14+
<h1>createWritable() mode test</h1>
15+
<ol>
16+
<li>
17+
Select a file to write to: <button class="select">Select file</button>
18+
</li>
19+
<li>
20+
<label for="filetext">Enter text to write to the file:</label>
21+
<input type="text" id="filetext" name="filetext" disabled />
22+
</li>
23+
<li>
24+
Write your text to the file:
25+
<button class="write" disabled>Write text</button>
26+
</li>
27+
</ol>
28+
<footer>
29+
<p>
30+
Check out the
31+
<a
32+
href="https://github.com/mdn/dom-examples/tree/main/file-system-api/createwritable-mode"
33+
>source code</a
34+
>.
35+
</p>
36+
</footer>
37+
<script>
38+
const selectBtn = document.querySelector(".select");
39+
const writeBtn = document.querySelector(".write");
40+
const fileText = document.querySelector("#filetext");
41+
42+
let writableStream = null;
43+
44+
async function selectFile() {
45+
// Create a new handle
46+
const handle = await window.showSaveFilePicker();
47+
48+
// Create a FileSystemWritableFileStream to write to
49+
try {
50+
writableStream = await handle.createWritable({
51+
keepExistingData: true,
52+
mode: "exclusive",
53+
});
54+
} catch (e) {
55+
if (e.name === "NoModificationAllowedError") {
56+
console.log(
57+
`You can't access that file right now; someone else is trying to modify it. Try again later.`
58+
);
59+
} else {
60+
console.log(e.message);
61+
}
62+
}
63+
64+
// Enable text field and write button, disable select button
65+
fileText.disabled = false;
66+
writeBtn.disabled = false;
67+
selectBtn.disabled = true;
68+
}
69+
70+
async function writeFile() {
71+
// Write text to our file and empty out the text field
72+
await writableStream.write(fileText.value);
73+
fileText.value = "";
74+
75+
// Close the file and write the contents to disk.
76+
await writableStream.close();
77+
78+
// Disable text field and write button, enable select button
79+
fileText.disabled = true;
80+
writeBtn.disabled = true;
81+
selectBtn.disabled = false;
82+
83+
// Set writableStream back to null
84+
writableStream = null;
85+
}
86+
87+
selectBtn.addEventListener("click", selectFile);
88+
writeBtn.addEventListener("click", writeFile);
89+
</script>
90+
</body>
91+
</html>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
7+
<title>FileSystemHandle.remove() demo</title>
8+
9+
<!-- Import the webpage's stylesheet -->
10+
<link rel="stylesheet" href="style.css" />
11+
12+
<!-- Import the webpage's javascript file -->
13+
<script src="script.js" defer></script>
14+
</head>
15+
<body>
16+
<h1>FileSystemHandle.remove() demo</h1>
17+
18+
<div>
19+
<p>
20+
<label for="file-text">Enter text for your new file:</label>
21+
</p>
22+
<textarea id="file-text" rows="8" cols="50"></textarea>
23+
</div>
24+
25+
<div>
26+
<button class="save">Save file</button>
27+
</div>
28+
29+
<h2>Saved files</h2>
30+
31+
<div>
32+
<ul class="save-list"></ul>
33+
</div>
34+
35+
<hr />
36+
37+
<footer>
38+
<p>
39+
Check out the
40+
<a
41+
href="https://github.com/mdn/dom-examples/tree/main/file-system-api/filesystemhandle-remove"
42+
>source code</a
43+
>.
44+
</p>
45+
</footer>
46+
</body>
47+
</html>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
const textarea = document.querySelector("textarea");
2+
const saveBtn = document.querySelector(".save");
3+
const saveList = document.querySelector(".save-list");
4+
5+
// Array to store handles of created files in
6+
let savedFileRefs = [];
7+
8+
// Create a new text file handle
9+
async function getNewFileHandle() {
10+
const opts = {
11+
types: [
12+
{
13+
description: "Text file",
14+
accept: { "text/plain": [".txt"] },
15+
},
16+
],
17+
};
18+
return await window.showSaveFilePicker(opts);
19+
}
20+
21+
// Write the provided text to the provided file handle
22+
async function writeFile(fileHandle, contents) {
23+
// Create a FileSystemWritableFileStream to write to.
24+
const writable = await fileHandle.createWritable();
25+
26+
// Write the contents of the file to the stream.
27+
await writable.write(contents);
28+
29+
// Close the file and write the contents to disk.
30+
await writable.close();
31+
}
32+
33+
// Update the app and UI to contain details of a new file
34+
async function processNewFile(fileHandle) {
35+
// Check whether the file the text was written to already exists on
36+
// disk. If so, don't create a new entry in the app
37+
for (const handle of savedFileRefs) {
38+
if (handle.name === fileHandle.name) {
39+
alert("New text written to existing file");
40+
return;
41+
}
42+
}
43+
44+
// Push the new file handle onto the array
45+
savedFileRefs.push(fileHandle);
46+
47+
// Create a new list item with delete button in the UI
48+
const listItem = document.createElement("li");
49+
listItem.textContent = fileHandle.name;
50+
const deleteBtn = document.createElement("button");
51+
deleteBtn.textContent = "Delete";
52+
53+
// The button id is the file name, with the .txt part removed
54+
const fileId = fileHandle.name.split(".")[0];
55+
deleteBtn.id = fileId;
56+
saveList.appendChild(listItem);
57+
listItem.appendChild(deleteBtn);
58+
59+
// Add an event handler to the button so the file is deleted when pressed
60+
deleteBtn.addEventListener("click", await deleteFile);
61+
}
62+
63+
// Delete a file
64+
async function deleteFile(e) {
65+
// Walk through each file handle in the array
66+
for (const handle of savedFileRefs) {
67+
// When we find a match between a stored handle and the button id
68+
if (handle.name === e.target.id + ".txt") {
69+
// Remove the file from the file system
70+
await handle.remove();
71+
// remove the file handle reference form the array
72+
savedFileRefs = savedFileRefs.filter(
73+
(handle) => handle.name !== e.target.id + ".txt"
74+
);
75+
// Delete the corresponding list item from the UI
76+
e.target.parentElement.parentElement.removeChild(e.target.parentElement);
77+
}
78+
}
79+
}
80+
81+
// Pressing the Save file button kicks everything off
82+
saveBtn.addEventListener("click", async () => {
83+
// If the textarea is not empty
84+
if (textarea.value !== "") {
85+
// Create a new file handle
86+
const fileHandle = await getNewFileHandle();
87+
// Write the textarea contents to it
88+
await writeFile(fileHandle, textarea.value);
89+
// Update the app with the new file details
90+
await processNewFile(fileHandle);
91+
}
92+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
h1,
2+
h2,
3+
p,
4+
li {
5+
font-family: sans-serif;
6+
}
7+
8+
li {
9+
margin-bottom: 10px;
10+
}
11+
12+
li button {
13+
margin-left: 10px;
14+
}

0 commit comments

Comments
 (0)