javascript - Download files from OPFS - Stack Overflow

let's say I have code that writes some stuff to a file in OPFS. What's the best way to let th

let's say I have code that writes some stuff to a file in OPFS. What's the best way to let the user download that file to their Downloads folder?

I'm aware that I can create a blob and attach it to the href attribute of an a tag. I just wonder if that's the only/best option, especially if the file is rather big, say, a couple of gigabytes.

Thanks.

let's say I have code that writes some stuff to a file in OPFS. What's the best way to let the user download that file to their Downloads folder?

I'm aware that I can create a blob and attach it to the href attribute of an a tag. I just wonder if that's the only/best option, especially if the file is rather big, say, a couple of gigabytes.

Thanks.

Share Improve this question asked Mar 27 at 1:00 Deling RenDeling Ren 2072 silver badges6 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Technically, if the file was written to the Origin Private File System, on Chromium-based browsers, at least, the file is already on the users' machine, and can be read, and written to a different directory on the users' own machine, outside of the browser, or inside of the browser using a Web extension.

Here's some code to read the Origin Private File System on Chromium, and write that folder somewhere else https://gist.github/guest271314/78372b8f3fabb1ecf95d492a028d10dd#file-createreadwritedirectoriesinbrowser-js-L118-L160

  // Helper function for filesystem *development*
  // Get directory in origin private file system from Chrome configuration folder.
  // fetch() file: protocol with "file://*/*" or "<all_urls>" in "host_permissions"
  // in browser extension manifest.json
  async function parseChromeDefaultFileSystem(path) {
    try {
      const set = new Set([
        32, 45, 46, 47, 48, 49, 50, 51, 52, 53,
        54, 55, 56, 57, 58, 64, 65, 66, 67, 68,
        69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
        79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
        89, 90, 95, 97, 98, 99, 100, 101, 102, 
        103, 104, 105, 106, 107, 108, 109, 110,
        111, 112, 113, 114, 115, 116, 117, 118,
        119, 120, 121, 122,
      ]);
      const request = await fetch(path);
      const text = (await request.text()).replace(/./g, (s) => set.has(s.codePointAt()) ? s : "");
      const files = [
        ...new Set(
          text.match(
            /00000\d+[A-Za-z-_.0-9\s]+\.crswap/g,
          ),
        ),
      ].map((s) => {
        const dir = [...new Set(text.slice(0, text.indexOf(s)).match(/(?<=[@\s]|CHILD_OF:0:)([\w-_])+(?=Ux)/g).map((d) =>
          d.split(/\d+|D140/)
        ))].flat().pop();
        const re = /00000[\d\s]+|\.crswap/g;
        const [key] = s.match(re);
        return ({
          [key]: s.replace(re, ""),
          dir
        })
      });
      return {
        name: files[0].dir,
        files
      }
    } catch (e) {
      console.error(e);
    }
  }  

On Chromium-based browsers, where showOpenFilePicker() and showSaveFilePicker() are defined, the file can be streamed to the file system by creating a FileSystemWritableStream, and then using pipeTo() to the created FileSystemFileHandle.

Something like this

let writable = await fileSystemHandle.createWritable();
await readable.pipeTo(writable).catch(console.log);

This is what I do to download node nightly completely in the browser https://github/guest271314/download-node-nightly-executable/blob/main/index.html#L50C9-L63C12

        const untarFileStream = new UntarFileStream(buffer);
        while (untarFileStream.hasNext()) {
          file = untarFileStream.next();
          if (/\/bin\/node$/.test(file.name)) {
            break;
          }
        }
        writable = await fileSystemHandle.createWritable();
        writer = writable.getWriter();
        await writer.write(file.buffer);
        await writer.close();
        new Notification('Download complete.', {
          body: `Successfully downloaded node executable ${version}`
        });

You can try to download a GB file on non-Chromium-based browsers, using HTML <a> element. Might work, depending on the browser and how much space is on the device. And user download preferences. See How to download a file without using <a> element with download attribute or a server?.

You can create a FormData and the user can download that attachment.

There's a few ways to write files to specifica folders from the browser. Some hacks. Some experimental.

I wrote to do exactly that, using Native Messaging, from the browser executing a local application, here using QuickJS https://github/guest271314/native-messaging-file-writer/tree/quickjs

const {
  UntarFileStream
} = await import(URL.createObjectURL(new Blob([await (await fetch("https://gist.githubusercontent/guest271314/93a9d8055559ac8092b9bf8d541ccafc/raw/022c3fc6f0e55e7de6fdfc4351be95431a422bd1/UntarFileStream.js")).bytes()], {
  type: "text/javascript"
})));

const cors_api_host = "corsproxy.io/?url=";
const cors_api_url = "https://" + cors_api_host;
let osArch = "linux-x64";
let file;

let [node_nightly_build] = await (await fetch("https://nodejs./download/nightly/index.json")).json();
let {
  version,
  files
} = node_nightly_build;
let node_nightly_url = `https://nodejs./download/nightly/${version}/node-${version}-${osArch}.tar.gz`;
let url = `${cors_api_url}${node_nightly_url}`;
console.log(`Fetching ${node_nightly_url}`);
const request = (await fetch(url)).body.pipeThrough(new DecompressionStream('gzip'));
// Download gzipped tar file and get ArrayBuffer
const buffer = await new Response(request).arrayBuffer();
// Decompress gzip using pako
// Get ArrayBuffer from the Uint8Array pako returns
// const decompressed = await pako.inflate(buffer);
// Untar, js-untar returns a list of files
// (See https://github/InvokIT/js-untar#file-object for details)
const untarFileStream = new UntarFileStream(buffer);
while (untarFileStream.hasNext()) {
  file = untarFileStream.next();
  if (/\/bin\/node$/.test(file.name)) {
    break;
  }
}

var stream = new Blob([file.buffer]).stream();

var {
  externalController,
  progressStream
} = await connectExternalFileWriter(
  "/home/user/native-messaging-file-writer-quickjs",
  "/home/user/Downloads/node",
  ["O_RDWR", "O_CREAT", "O_TRUNC"],
  "0o744"
).catch(console.error);
// externalController.error("a reason");
// externalController.close();
progressStream.pipeTo(new WritableStream({
  start() {
    console.groupCollapsed("FileWriter progress");
  },
  write(v) {
    console.log(v);
  },
  close() {
    console.groupEnd("FileWriter progress");
  },
  abort(reason) {
    console.log(reason);
    console.groupEnd("FileWriter progress");
  }
}), ).catch(console.error);

var writeStream = stream.pipeTo(new WritableStream({
    write(v) {
      externalController.enqueue(v);
    },
    close() {
      externalController.close();
    },
  }));

writeStream.catch(console.error);
#!/usr/bin/env -S /home/user/bin/qjs -m
// QuickJS Native Messaging host
// guest271314, 5-6-2022
import * as std from "qjs:std";
import * as os from "qjs:os";

function getMessage() {
  const header = new Uint32Array(1);
  std.in.read(header.buffer, 0, 4);
  const output = new Uint8Array(header[0]);
  std.in.read(output.buffer, 0, output.length);
  return output;
}

function sendMessage(message) {
  const header = Uint32Array.from(
    {
      length: 4,
    },
    (_, index) => (message.length >> (index * 8)) & 0xff,
  );
  const output = new Uint8Array(header.length + message.length);
  output.set(header, 0);
  output.set(message, 4);
  std.out.write(output.buffer, 0, output.length);
  std.out.flush();
}

function encodeMessage(message) {
  return new Uint8Array(
    [...JSON.stringify(message)].map((s) => s.codePointAt()),
  );
}

function main() {
  let file = void 0;
  let writes = 0;
  let totalBytesWritten = 0;
  const buffer = new ArrayBuffer(0, {maxByteLength:16384});
  const view = new DataView(buffer);
  while (true) {
    const message = getMessage();
    const str = String.fromCodePoint(...message);
    const { value, done } = JSON.parse(str);
    if (file === undefined) {
      file = os.open(value.fileName, value.flags.reduce((a, b) => (os[a] || a) | os[b]), value.mode);
      continue;
    }
    if (done) {
      os.close(file);
      if (buffer.byteLength > 0) {
        buffer.resize(0);
      }
      sendMessage(
        new Uint8Array(
          encodeMessage({ done, value, totalBytesWritten }),
        ),
      );
      break;
    } else {
      buffer.resize(value.length);
      for (let i = 0; i < value.length; i++) {
        view.setUint8(i, value.at(i));
      }
      const currentBytesWritten = os.write(file, buffer, 0, buffer.byteLength);
      buffer.resize(0);
      totalBytesWritten += currentBytesWritten;
      ++writes;
      sendMessage(
        encodeMessage({
          done,
          writes,
          currentBytesWritten,
          totalBytesWritten,
        }),
      );
    }
  }
}

try {
  main();
} catch (e) {
  std.exit(1);
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744118262a4559276.html

相关推荐

  • javascript - Download files from OPFS - Stack Overflow

    let's say I have code that writes some stuff to a file in OPFS. What's the best way to let th

    8天前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信