leaningtech / cheerpj-meta

Run Java 8 applications, libraries, applets, Java Web Start, and Oracle Forms on the web without legacy plugins.
https://labs.leaningtech.com/cheerpj
445 stars 21 forks source link

Mount local files to the file system #98

Open oeway opened 3 years ago

oeway commented 3 years ago

Being able to mount TypedArray under /str is very practical feature, and this is how I send data to the java app.

However, if it's a huge file (e.g. several GB), it's not possible to load them into the memory. Ideally I would like to mount directly a file or a folder that I selected or drag'n'drop into the file system. This will hopefully enable reading file content on demand.

For now it should be possible to build that as a readonly file system, and in the future it will be possible to use the Native File System api to support writing.

Do you have a quick solution for this? Otherwise I may try to hack around by following the example you have for CheerpJDataFolder.

alexp-sssup commented 3 years ago

There is no easy solution for this. The API you mentioned is not supported by anybody in practice: https://caniuse.com/#feat=native-filesystem-api.

We have some ideas on how this could be implemented, but there are complexities both on the technical side (i.e. how to access the files from the browser sandbox) and the UX side (How to let the user select the files from without breaking the consistency of the virtualized file systems)

oeway commented 3 years ago

Thanks for the answer. I agree both challenges exists, however I think at least we can aim for mounting the files and folders that already exposed to the browser sandbox. That will enable accessing to large files on disk (read only) which is critical for my application.

For the UX, what I have in mind is to display a folder on the virtual desktop where user can select more files or drag and drop files to that folder, these files will then be seen in the java app. This isn’t ideal, but at least allow the work to be done.

alexp-sssup commented 3 years ago

In the short term what we could offer is to support more data types (beside String and Uint8Array) in the /str/ filesystem. We could for example allow to mount File objects so that they could be read progressively. As usual how much priority this additional feature gets depends on the commercial interest for it.

oeway commented 3 years ago

In the short term what we could offer is to support more data types (beside String and Uint8Array) in the /str/ filesystem. We could for example allow to mount File objects so that they could be read progressively. As usual how much priority this additional feature gets depends on the commercial interest for it.

Sounds good, thanks!

oeway commented 3 years ago

@alexp-sssup I am trying to patch the /str mount here, the idea is to provide a strReadAsync function that will dynamically read requested chunks with a FileReader. However, I do not fully understand how the buildContinuations part work, and I couldn't get it to continue. Could you please take a look at this function: strReadAsync? Any hint will be appreciated.

oeway commented 3 years ago

@alexp-sssup I tried for quite a while on this, but couldn't make it through. Please take a look at the strReadAsync, see if you can spot any obvious issue:

  function strReadAsync(fileData, fileOffset, buf, off, len, flags, p)
  {
    assert(len != 0);
    const file = fileData.parent.files[fileData.path]
    const fileChunk = file.slice(fileOffset, len + fileOffset);
    const fileRef = {fileChunk: fileChunk, readLength: 0}

    function readFile(fileRef, buf, a){
      const r = new FileReader();
      r.thread = currentThread;
      r.onload = function(evt) {
        if (evt.target.error == null) {
          buf.set(evt.target.result);
          fileRef.readLength = evt.target.result.length
          cheerpjWakeThread(r.thread)
        }
      }
      r.readAsArrayBuffer(fileRef.fileChunk);
      cheerpjPauseThread(a)
    }

    const a={p: p, f:strReadAsync,pc:0,fileData:fileData,fileOffset:fileOffset,buf:buf,off:off, flags:flags,len:len};
    a.pc=0; readFile(fileRef, buf, a);
    return fileRef.readLength;
  }

// use strReadAsync
const StrOps = { statAsync: strStatAsync, listAsync: strListAsync, makeFileData: strMakeFileData, createDirAsync: null, loadAsync: strLoadAsync, renameAsync: null, linkAsync: null, unlinkAsync: null };
const strInodeOps = {readAsync: strReadAsync, writeAsync: null, ioctlAsync: null, commitFileData: null, readPoll: null }
...