-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New File System API #11018
Comments
This is blocked on #10969 |
The example creates a blob and does nothing with... const blob = new Blob(["Hello World"]);
const fileHandle = await Deno.getFileHandle("/tmp/foo.txt"); // Returns FileSystemFileHandle
const writable = await fileHandle.createWritable();
const writer = writable.getWriter();
await writer.write(new TextEncoder().encode("Hello World"));
await writer.close(); |
Maybe this could return the cwd (or a complete sandboxed path?): navigator.storage.getDirectory() It's your own sandboxed directory that you can read/write to without any permission promps the |
The As I mentioned in #2456 (comment), it would be great if code like this worked in Deno: let dir = await globalThis.showDirectoryPicker();
let file = await dir.getFileHandle("hello.jpg", { create: true });
await blobToSave.stream().pipeTo(await file.createWritable()); In the browser, the above code triggers a directory picker (like a normal save-as dialogue) and then two permission prompts (one for reading, and one for writing). I'm not sure if there's a common UX/UI for directory-picking in the terminal (other than pasting a file path), but I like ncdu's simple/obvious UI - up/down to move between files and folders in a directory, and right/left to move into and out of a folder. For UX/UI reference, here's the dialogue after selecting a directory called "inpaint" in the directory picker: And here's the dialogue after attempting to write content to that directory: I haven't used permission requests in Deno, but I imagine the UX flow could be similar? (Unless the program already has permission to read/write to that directory). I like that the file/directory picker is separated from actually asking for permission to read/write to the selected path, because it makes it harder to accidentally give unintended permissions to a program. |
Mozilla seems to have marked File System Access API as 'harmful' recently mozilla/standards-positions#545 though they say there's a subset of the API they're quite enthusiastic about. |
I think it is just the method of opening files, because they don't consider the permission prompts strict enough. The actual interface seems to be relatively uncontroversial. |
so started implementing this, and its not great a great API for the case of proper fs access; especially interaction with directories. removeEntry isnt great: you cant really delete the dir the directoryhandle is in, and traversing directories is horrible + you cant go up a directoryhandle. paths for all the api arent allowed but just filenames and one is supposed to traverse dirs by async iter/the name of the dir, and as such the whole api is just designed around this concept. this and some others things are just convoluted to use. sure, some of these things can be worked around/ignore parts of the spec, but it just seems like it will be a horrible user experience for usage in deno. this api is not suitable for what we want to replace. |
@crowlKats This is interesting - do you have an insight into why the API's intention differs from what Deno would want? A lot of smart people worked on the API, and it has gone through several iterations, so presumably a bunch of thought has gone into the API design, and so I'm curious if you have any thoughts on whether this API is badly designed for the browser use-case too? Or do you think there some key difference between the two environments (Deno vs browser) that makes it fine for the browser use case, but bad for Deno?
Is this perhaps just a general feature of a low-level file system API? I.e. do these concepts of directory handles and file names map more closesly to OS filesystem APIs than the paths stuff you mentioned? I don't have a lot of experience with file system stuff, so I'm mostly just guessing here.
I think the API is still in an MVP/V1 stage - see issues here: V2 milestone. Amongst those V2 issues there is a discussion around accessing the parent directory from a directory handle, for example. |
The API is fine for it's intended use; on a browser you arent going to do what one would do in deno in regards to interacting with the file system. Or rather, for a small set if directories, this API is fine, but then moment you want to do something with multiple nested directories, it gets quite painful.
What i mean is:
Oh thanks for pointing that out. I see there are some problems being discussed that I mentioned. Though even that doesn't really fix everything to make this viable in my opinion, given my other points. |
For this ry/me suggested |
I have also started working on a PR for native file system, i think it's grate in such a way that it's more safer than other. I have already made the most work and adapted most of https://github.com/jimmywarting/native-file-system-adapter to fit deno's eco system more and already have some (own) test passing As for the removing the directory from itself: WICG/file-system-access#283 |
What concerns me more than directory traversal is that all writes unconditionally happen via a temporary file. The underlying file is updated atomically on close of the |
yea, it's a concern many ppl have over the API and is highly discussed upon in their issues. regarding copying/moving and atomic writes |
Next problem: |
Nah, it dose not do a disk copy, it only stat's the for file size and last modified date. My idea was that |
@jimmywarting That is not safe though.
|
That can happen in the browser also from if the file is removed, or the name, size or modifiedDate change when u call They do not copy the file: WICG/file-system-access#101 (comment) if you get a file from |
This is a huge issue though, as it is racy:
What happens if I now shrink the file while the streaming of the file is still ongoing? Is it going to complete, but return the same number of bytes that |
I'm not that technical... i do not have any knowledge how the browser dose it. All i know is that it dose not copy a file Either way i think it's important that we can receive a I really hope for a |
[Ed: @ry's proposal is already using File System Handle's, looks great.] Access Handle is being developed to allow more direct access to files in File System Access specification, get away from the various atomic writes et cetera problems folks have mentioned. There's also Storage Foundation API that has been under development, which similarly has intended to provide lower level access. There's ongoing discussion about how possibly these initiatives might merge. Edit: the Access Handle and Storage Foundation (aka Origin Private File System/OPFS) work appears to be getting moved to WHATWG. See wicg/FSA#342 and whatwg/sg#171. 🤞 |
Just chiming in here again to say that I've been writing a lot of little utility scripts in the past few weeks using the File System Access API (to clean datasets, reorganise files, etc.) and they all first prompt for a directory to base themselves out of with let dirHandle = await window.showDirectoryPicker(); It would be really nice if these scripts worked "out of the box" with Deno. This would require an interactive input, like I mention this again not because it's an immediate concern, but just so that (I hope) Deno doesn't don't go through any one-way doors with the design which would prevent this sort of thing from being implemented in the future. (Please ignore this comment if there's no risk of this) |
Given that Safari now supports OPFS, and FileSystemSyncAccessHandle seems to address most of the concerns @lucacasonato raised here, perhaps it's worth another look? |
firefox also have OPFS now |
Wanted to share my experience around this issue. I'm currently working on an SDK which makes // Node.js v20+
import fs from "node:fs";
async function run() {
const file = await fs.openAsBlob("./sample.txt");
const fd = new FormData();
fd.append("file", file);
const res = await fetch("https://httpbin.org/anything/upload", {
method: "POST",
body: fd,
});
console.log(await res.json());
}
run(); // Node.js v18
import { fileFrom } from "fetch-blob/from.js";
async function run() {
const file = await fileFrom("./sample.txt");
const fd = new FormData();
fd.append("file", file);
const res = await fetch("https://httpbin.org/anything/upload", {
method: "POST",
body: fd,
});
console.log(await res.json());
}
run(); // Bun
async function run() {
const file = Bun.file("./sample.txt");
const fd = new FormData();
fd.append("file", file);
const res = await fetch("https://httpbin.org/anything/upload", {
method: "POST",
body: fd,
});
console.log(await res.json());
}
run(); What's especially interesting is how the third-party library I'm wondering if Deno's FormData type can lean more on duck typing to permit third-party, Blob-like objects or perhaps introduce |
Hi @disintegrator
i think durning this lifespan i saw some who tried create there own version of disc based blob/files before they discovered that fetch-blob existed. this method have some slight more advantages. b/c they are instances of built in blob and are therefore more acceptable to other tools that use them. i did something a while back to try and get something like disc based blob/files in deno a while back export const openAsFile = async fileUrl => {
const url = new URL(fileUrl)
const basename = url.pathname.split('/').pop()
const fsFile = await Deno.open(url)
const stat = await fsFile.stat()
fsFile.close()
if (!stat.isFile) throw Error('not found')
return new DiskFile([], basename, { path: url, stat })
}
const DiskFile = class File extends globalThis.File {
#path = ''
size = 0
lastModified = 0
constructor (fileUrl, fileName, options) {
super([], fileName, options)
const stat = options.stat
this.#path = options.path
this.size = stat.size
this.lastModified = +stat.mtime
}
stream() {
return Deno.openSync(this.#path).readable
}
async text() {
let str = ''
const ts = this.stream().pipeThrough(new TextDecoderStream())
for await (let chunk of ts) str += chunk
return str
}
async arrayBuffer() {
// TODO: handle sliced blob
const fsFile = await Deno.open(this.#path)
// TODO: validate that the file have not changed.
// const stat = await fsFile.stat()
const uint8 = new Uint8Array(this.size)
return uint8.buffer
}
slice (start, end, type = '') {
throw new Error('Not impl.')
return new DiskFile()
}
} very incomplete but at least gets the job done. |
This API would live in parallel with the existing low-level file system API provided by
Deno.open
,Deno.write
,Deno.read
,Deno.readDir
, ...Writing to a file:
Also directory listing
https://wicg.github.io/file-system-access/
Or maybe we have a Deno name-spaced directoryHandle corresponding to the cwd.
The text was updated successfully, but these errors were encountered: