Skip to content

Commit c3e0512

Browse files
committed
feat: fs.readFile and tests working with tauri node ws fs
1 parent da32628 commit c3e0512

7 files changed

Lines changed: 316 additions & 181 deletions

File tree

dist/phoenix-fs.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@ const { exec } = require('child_process');
66

77
const IS_MACOS = os.platform() === 'darwin';
88

9+
/**
10+
* Converts a buffer to an `ArrayBuffer`.
11+
*
12+
* @param {Buffer} buf The buffer to convert
13+
* @return {ArrayBuffer} Converted buffer
14+
* @public
15+
*/
16+
function toArrayBuffer(buf) {
17+
if (buf.length === buf.buffer.byteLength) {
18+
return buf.buffer;
19+
}
20+
21+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
22+
}
23+
924
function getWindowsDrives(callback) {
1025
exec('wmic logicaldisk get name', (error, stdout) => {
1126
if (error) {
@@ -89,7 +104,8 @@ const WS_COMMAND = {
89104
CONTROL_SOCKET_ANNOUNCE: "controlSock",
90105
GET_WINDOWS_DRIVES: "getWinDrives",
91106
READ_DIR: "readDir",
92-
STAT: "stat"
107+
STAT: "stat",
108+
READ_BIN_FILE: "readBinFile"
93109
};
94110

95111
const LARGE_DATA_THRESHOLD = 2*1024*1024; // 2MB
@@ -196,6 +212,14 @@ function _stat(ws, metadata) {
196212
}).catch((err)=>_reportError(ws, metadata, err, `Failed to get stat for ${fullPath}`));
197213
}
198214

215+
function _readBinaryFile(ws, metadata) {
216+
const fullPath = metadata.data.path;
217+
fs.readFile(fullPath)
218+
.then(data => {
219+
_sendResponse(ws, metadata, {}, toArrayBuffer(data));
220+
}).catch((err)=>_reportError(ws, metadata, err, `Failed to read file at path ${fullPath}`));
221+
}
222+
199223
function processWSCommand(ws, metadata, dataBuffer) {
200224
try{
201225
switch (metadata.commandCode) {
@@ -214,6 +238,9 @@ function processWSCommand(ws, metadata, dataBuffer) {
214238
case WS_COMMAND.STAT:
215239
_stat(ws, metadata);
216240
return;
241+
case WS_COMMAND.READ_BIN_FILE:
242+
_readBinaryFile(ws, metadata);
243+
return;
217244
case WS_COMMAND.LARGE_DATA_SOCKET_ANNOUNCE:
218245
console.log("Large Data Transfer Socket established, socket Group: ", metadata.socketGroupID);
219246
ws.isLargeData = true;

dist/virtualfs.js

Lines changed: 176 additions & 175 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/virtualfs.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/node-src/phoenix-fs.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@ const { exec } = require('child_process');
66

77
const IS_MACOS = os.platform() === 'darwin';
88

9+
/**
10+
* Converts a buffer to an `ArrayBuffer`.
11+
*
12+
* @param {Buffer} buf The buffer to convert
13+
* @return {ArrayBuffer} Converted buffer
14+
* @public
15+
*/
16+
function toArrayBuffer(buf) {
17+
if (buf.length === buf.buffer.byteLength) {
18+
return buf.buffer;
19+
}
20+
21+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
22+
}
23+
924
function getWindowsDrives(callback) {
1025
exec('wmic logicaldisk get name', (error, stdout) => {
1126
if (error) {
@@ -89,7 +104,8 @@ const WS_COMMAND = {
89104
CONTROL_SOCKET_ANNOUNCE: "controlSock",
90105
GET_WINDOWS_DRIVES: "getWinDrives",
91106
READ_DIR: "readDir",
92-
STAT: "stat"
107+
STAT: "stat",
108+
READ_BIN_FILE: "readBinFile"
93109
};
94110

95111
const LARGE_DATA_THRESHOLD = 2*1024*1024; // 2MB
@@ -196,6 +212,14 @@ function _stat(ws, metadata) {
196212
}).catch((err)=>_reportError(ws, metadata, err, `Failed to get stat for ${fullPath}`));
197213
}
198214

215+
function _readBinaryFile(ws, metadata) {
216+
const fullPath = metadata.data.path;
217+
fs.readFile(fullPath)
218+
.then(data => {
219+
_sendResponse(ws, metadata, {}, toArrayBuffer(data));
220+
}).catch((err)=>_reportError(ws, metadata, err, `Failed to read file at path ${fullPath}`));
221+
}
222+
199223
function processWSCommand(ws, metadata, dataBuffer) {
200224
try{
201225
switch (metadata.commandCode) {
@@ -214,6 +238,9 @@ function processWSCommand(ws, metadata, dataBuffer) {
214238
case WS_COMMAND.STAT:
215239
_stat(ws, metadata);
216240
return;
241+
case WS_COMMAND.READ_BIN_FILE:
242+
_readBinaryFile(ws, metadata);
243+
return;
217244
case WS_COMMAND.LARGE_DATA_SOCKET_ANNOUNCE:
218245
console.log("Large Data Transfer Socket established, socket Group: ", metadata.socketGroupID);
219246
ws.isLargeData = true;

src/fslib_node_ws.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ const WS_COMMAND = {
3434
CONTROL_SOCKET_ANNOUNCE: "controlSock",
3535
GET_WINDOWS_DRIVES: "getWinDrives",
3636
READ_DIR: "readDir",
37-
STAT: "stat"
37+
STAT: "stat",
38+
READ_BIN_FILE: "readBinFile"
3839
};
3940

4041
// each browser context belongs to a single socket group. So multiple websocket connections can be pooled
@@ -352,13 +353,27 @@ function stat(path, callback) {
352353
});
353354
}
354355

356+
function readBinaryFile(path) {
357+
return new Promise((resolve, reject)=>{
358+
const platformPath = Utils.getTauriPlatformPath(path);
359+
_execCommand(WS_COMMAND.READ_BIN_FILE, {path: platformPath})
360+
.then(({bufferData})=>{
361+
resolve(bufferData);
362+
})
363+
.catch((err)=>{
364+
reject(mapNodeTauriErrorMessage(err, path, 'Failed to read file: '));
365+
});
366+
});
367+
}
368+
355369
const NodeTauriFS = {
356370
testNodeWsEndpoint,
357371
setNodeWSEndpoint,
358372
stopNodeWSEndpoint,
359373
getNodeWSEndpoint,
360374
readdir,
361-
stat
375+
stat,
376+
readBinaryFile
362377
};
363378

364379
module.exports = {

src/fslib_tauri.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,21 @@ function _processContents(contents, encoding, callback, path) {
481481
function readFile(path, options, callback) {
482482
try {
483483
path = globalObject.path.normalize(path);
484-
const platformPath = Utils.getTauriPlatformPath(path);
485484

486485
callback = arguments[arguments.length - 1];
487486
options = Utils.validateFileOptions(options, Constants.BINARY_ENCODING, 'r');
488487

488+
if(!window.__TAURI__ || preferNodeWs) {
489+
NodeTauriFS.readBinaryFile(path)
490+
.then(contents => {
491+
// contents is Array buffer
492+
_processContents(contents, options.encoding, callback, path);
493+
})
494+
.catch(callback);
495+
return;
496+
}
497+
498+
const platformPath = Utils.getTauriPlatformPath(path);
489499
__TAURI__.fs.readBinaryFile(platformPath)
490500
.then(contents => {
491501
// contents is Uint8Array

test/test-file.browser.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,61 @@ function _setupTests(testType) {
493493
const err = await promise;
494494
expect(err.code).to.eql(fs.ERR_CODES.ECHARSET);
495495
});
496+
497+
function getRandomChar() {
498+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_+{}|:"<>?-=[]\\;\',./';
499+
return chars.charAt(Math.floor(Math.random() * chars.length));
500+
}
501+
502+
function createRandomString(length) {
503+
let str = '';
504+
for (let i = 0; i < length; i++) {
505+
str += getRandomChar();
506+
}
507+
return str;
508+
}
509+
510+
async function _testLargeFileRW(filePath, sizeBytes) {
511+
console.log("creating random string of size", sizeBytes);
512+
const stringWrittenToFile = createRandomString(sizeBytes);
513+
console.log("random string created");
514+
let resolveP, rejectP;
515+
const writePromise = new Promise((resolve, reject) => {resolveP = resolve; rejectP = reject;});
516+
fs.writeFile(filePath, stringWrittenToFile, "utf8", (_err)=>{
517+
if(_err){
518+
rejectP(_err);
519+
return;
520+
}
521+
resolveP();
522+
});
523+
await writePromise;
524+
525+
const readPromise = new Promise((resolve, reject) => {resolveP = resolve; rejectP = reject;});
526+
fs.readFile(filePath, "utf8", (_err, data)=>{
527+
if(_err){
528+
rejectP(_err);
529+
return;
530+
}
531+
resolveP(data);
532+
});
533+
const strReadFromFile = await readPromise;
534+
expect(strReadFromFile).to.eql(stringWrittenToFile);
535+
}
536+
537+
let sizeMBs = [1, 2, 4, 8, 16];
538+
if(location.href.startsWith("http://localhost:8081/test/")){
539+
// during development, large data transfer seems to get the developer tools stuck. so we only run small tests
540+
sizeMBs = [1, 2, 4];
541+
it(`Should phoenix ${testType} read and write a large text file with 8, 16 MB disabled in dev builds due to possible dev tools crash`, async function () {
542+
expect(1).to.eql(1);
543+
});
544+
}
545+
for(let sizeMB of sizeMBs) {
546+
it(`Should phoenix ${testType} read and write a large text file of size ${sizeMB} MB`, async function () {
547+
const filePath = `${testPath}/browserWrite.txt`;
548+
await _testLargeFileRW(filePath, sizeMB * 1024 * 1024); // Roughly 4MB considering 1 byte per char
549+
}).timeout(60000);
550+
}
496551
}
497552

498553
describe(`File: Browser virtual fs tests: filer paths`, function () {

0 commit comments

Comments
 (0)