Write files directly to local file system from arbitrary Web pages. Use externally_connectable to connect to the MV3 ServiceWorker from arbitrary Web pages. In the MV3 ServiceWorker use sendNativeMessage() to start local Node.js built-in node:http2 HTTP/2 server. Make fetch() request from Web page to local server, with duplex set to "half" for the capability to set a ReadableStream as body for POST, and/or query request. Write file to file system. Return Promise fulfilled to an <fs.Stats> object, or message from DOMException ABORT_ERR.
- Emphasising the importance of in-place writes #260
- Native FS writer makes write changes to a temp file instead of the actual file
Create self-signed certificates for use with HTTP/2 server, as described here
# As an alternative, Chromium can be instructed to trust a self-signed
# certificate using command-line flags. Here are step-by-step instructions on
# how to do that:
#
# 1. Generate a certificate and a private key:
# openssl req -newkey rsa:2048 -nodes -keyout certificate.key \
# -x509 -out certificate.pem -subj '/CN=Test Certificate' \
# -addext "subjectAltName = DNS:localhost"
#
# 2. Compute the fingerprint of the certificate:
# openssl x509 -pubkey -noout -in certificate.pem |
# openssl rsa -pubin -outform der |
# openssl dgst -sha256 -binary | base64
# The result should be a base64-encoded blob that looks like this:
# "Gi/HIwdiMcPZo2KBjnstF5kQdLI5bPrYJ8i3Vi6Ybck="
#
# 3. Pass a flag to Chromium indicating what host and port should be allowed
# to use the self-signed certificate. For instance, if the host is
# localhost, and the port is 4433, the flag would be:
# --origin-to-force-quic-on=localhost:4433
#
# 4. Pass a flag to Chromium indicating which certificate needs to be trusted.
# For the example above, that flag would be:
# --ignore-certificate-errors-spki-list=Gi/HIwdiMcPZo2KBjnstF5kQdLI5bPrYJ8i3Vi6Ybck=
#
# See https://www.chromium.org/developers/how-tos/run-chromium-with-flags for
# details on how to run Chromium with flags.
Clone repository
git clone https://github.com/guest271314/native-messaging-file-writer
Install Native Messaging host manifest (see Native manifests) to Chromium or Chrome user data directory. install-host.js currently points to ~/.config/chromium. Modify to use path to the version of chrome being used, e.g., ~/.config/google-chrome
node install-host.js
- Navigate to
chrome://extensions. - Toggle
Developer mode. - Click
Load unpacked. - Select
native-messaging-file-writerfolder. - Note the generated extension ID.
- Open
nm_file_writer.jsonin a text editor, set"path"to absolute path ofnm_file_writer.jsandchrome-extension://<ID>/using ID from 5 in"allowed_origins"array. - Copy the
nm_file_writer.jsonfile to Chrome or Chromium configuration folder, e.g., Chromium on Linux~/.config/chromium/NativeMessagingHosts; Chrome dev channel on Linux~/.config/google-chrome-unstable/NativeMessagingHostsUser Data Directory - Default Location. - Set
nm_file_writer.jspermission to executable, e.g.,chmod u+x nm_file_writer.js. - Reload the extension.
file-writer.js is a content script that defines FileWriter class on all HTTP/HTTPS Web pages at document_start.
Use FileWriter class in DevTools, Snippets, or other user scripts. The constructor expects an options plain JavaScript object containing fileName, mode (Sets the file mode (permission and sticky bits) if the file is created. Default: 0o666 (readable and writable) that are passed to a Node.js filehandle.createWriteStream().
var readable = ... // WHATWG ReadableStream
var fs = new FileWriter({
fileName: "/home/user/Downloads/node", // File path to write to
mode: 0o764 // Mode
}, "/home/user/native-messaging-file-writer"); // Path to unpacked extension directory
fs.write(readable).then(console.log).catch(console.warn);
// Abort writing to the file
fs.abort("reason");
Using a TransformStream to write data to the file using a WritableStreamDefaultWriter
var rs = ...;
var {
readable,
writable
} = new TransformStream({
transform(value, c) {
c.enqueue(value);
},
flush() {
console.log("flush");
}
});
var writer = writable.getWriter();
writer.closed.then(console.log).catch(console.warn);
var fs = new FileWriter({
fileName: "/home/user/Downloads/node",
mode: 0o764
}, "/home/user/native-messaging-file-writer");
fs.write(readable).then(console.log).catch(console.warn);
console.log(writer.desiredSize);
// Abort writing to the file
// await writer.abort("Abort!").then(console.log).catch(console.warn);
console.log(writer.desiredSize);
for await (const data of rs) {
try {
await writer.ready;
await writer.write(data).catch(console.warn);
} catch (e) {
console.log(e);
break;
}
}
console.log(writer.desiredSize);
if (writer.desiredSize !== null) {
await writer.close().catch(console.warn);
}
When the file is written without errors or for the WHATWG fetch() request being aborted, ultimately fulfills to a stat() object with fileName included, e.g., writing node nightly to file system
{
"fileName": "/home/user/Downloads/node",
"dev": 27,
"mode": 33252,
"nlink": 1,
"uid": 1000,
"gid": 1000,
"rdev": 0,
"blksize": 4096,
"ino": 173609,
"size": 125675720,
"blocks": 245464,
"atimeMs": 1740956750631.332,
"mtimeMs": 1740979068259.272,
"ctimeMs": 1740979068259.272,
"birthtimeMs": 1740956750631.332
}
When the streaming request is aborted
fs.abort("Abort file stream");
// Or when using a WritableStreamDefaultWriter
await writer.abort("Abort file stream").then(console.log).catch(console.warn);
// Abort file stream
// The operation was aborted
Do What the Fuck You Want to Public License WTFPLv2