fix: add stats API and fix fd leak, closes #2323 (#2723)

This commit is contained in:
Huang Xin
2025-12-16 13:51:48 +08:00
committed by GitHub
parent 7063d62b13
commit 5850a16afd
4 changed files with 59 additions and 10 deletions
+7 -10
View File
@@ -396,26 +396,23 @@ export abstract class BaseAppService implements AppService {
if (!(await this.fs.exists(getDir(book), 'Books'))) {
await this.fs.createDir(getDir(book), 'Books');
}
if (
saveBook &&
!transient &&
(!(await this.fs.exists(getLocalBookFilename(book), 'Books')) || overwrite)
) {
const bookFilename = getLocalBookFilename(book);
if (saveBook && !transient && (!(await this.fs.exists(bookFilename, 'Books')) || overwrite)) {
if (/\.txt$/i.test(filename)) {
await this.fs.writeFile(getLocalBookFilename(book), 'Books', fileobj);
await this.fs.writeFile(bookFilename, 'Books', fileobj);
} else if (typeof file === 'string' && isContentURI(file)) {
await this.fs.copyFile(file, getLocalBookFilename(book), 'Books');
await this.fs.copyFile(file, bookFilename, 'Books');
} else if (typeof file === 'string' && !isValidURL(file)) {
try {
// try to copy the file directly first in case of large files to avoid memory issues
// on desktop when reading recursively from selected directory the direct copy will fail
// due to permission issues, then fallback to read and write files
await this.fs.copyFile(file, getLocalBookFilename(book), 'Books');
await this.fs.copyFile(file, bookFilename, 'Books');
} catch {
await this.fs.writeFile(getLocalBookFilename(book), 'Books', fileobj);
await this.fs.writeFile(bookFilename, 'Books', await fileobj.arrayBuffer());
}
} else {
await this.fs.writeFile(getLocalBookFilename(book), 'Books', fileobj);
await this.fs.writeFile(bookFilename, 'Books', fileobj);
}
}
if (saveCover && (!(await this.fs.exists(getCoverFilename(book), 'Books')) || overwrite)) {
@@ -347,6 +347,11 @@ export const nativeFileSystem: FileSystem = {
return false;
}
},
async stats(path: string, base: BaseDir) {
const { fp, baseDir } = this.resolvePath(path, base);
return await stat(fp, baseDir ? { baseDir } : undefined);
},
};
const DIST_CHANNEL = (process.env['NEXT_PUBLIC_DIST_CHANNEL'] || 'readest') as DistChannel;
@@ -218,6 +218,43 @@ const indexedDBFileSystem: FileSystem = {
request.onerror = () => reject(request.error);
});
},
async stats(path: string, base: BaseDir) {
const { fp } = this.resolvePath(path, base);
const db = await openIndexedDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction('files', 'readonly');
const store = transaction.objectStore('files');
const request = store.get(fp);
request.onsuccess = () => {
const result = request.result;
if (result) {
const content = result.content;
const size =
content instanceof Blob
? content.size
: typeof content === 'string'
? content.length
: content instanceof ArrayBuffer
? content.byteLength
: 0;
resolve({
isFile: true,
isDirectory: false,
size,
mtime: null,
atime: null,
birthtime: null,
});
} else {
reject(new Error(`File not found: ${fp}`));
}
};
request.onerror = () => reject(request.error);
});
},
};
export class WebAppService extends BaseAppService {
+10
View File
@@ -25,6 +25,15 @@ export type FileItem = {
size: number;
};
export type FileInfo = {
isFile: boolean;
isDirectory: boolean;
size: number;
mtime: Date | null;
atime: Date | null;
birthtime: Date | null;
};
export interface FileSystem {
resolvePath(path: string, base: BaseDir): ResolvedPath;
getURL(path: string): string;
@@ -39,6 +48,7 @@ export interface FileSystem {
createDir(path: string, base: BaseDir, recursive?: boolean): Promise<void>;
removeDir(path: string, base: BaseDir, recursive?: boolean): Promise<void>;
exists(path: string, base: BaseDir): Promise<boolean>;
stats(path: string, base: BaseDir): Promise<FileInfo>;
getPrefix(base: BaseDir): Promise<string>;
}