diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 0a24535..1aa4f31 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -132,12 +132,38 @@ const getDownloadUrl = (path: string, forceDownload?: boolean) => `/api/download
const FileDownload = ({ url }: { url: string }) => ;
const ZipDownload = ({ url, title = 'Download folder as ZIP', style }: { url: string, title?: string, style?: CSSProperties }) => ;
+let sizeCounter: number = 0;
+
+const sizeMeter: { [key: number]: string } = {
+ 0: 'KB',
+ 1: 'MB',
+ 2: 'GB',
+ 3: 'TB'
+};
+
+// Format the byte
+const manageByte = (num: number): string | number => {
+ if (!num) return 0;
+ let res: number = num / 1024;
+
+ if (res > 1000) {
+ sizeCounter++;
+ return manageByte(res);
+ } else {
+ const value: number = sizeCounter;
+ sizeCounter = 0;
+ return res.toFixed(2) + sizeMeter[value];
+ }
+}
+
+
const FileRow = ({ path, isDir, fileName, onCheckedChange, checked }: {
path: string,
isDir: boolean,
fileName: string,
onCheckedChange?: ChangeEventHandler,
checked?: boolean | undefined,
+ size?: number | undefined,
}) => {
const Icon = isDir ? FaFolder : FaFileAlt;
@@ -148,12 +174,14 @@ const FileRow = ({ path, isDir, fileName, onCheckedChange, checked }: {
<>
{fileName} {fileName === '..' && (parent dir)}
+ {size && manageByte(size)}
>
) : (
<>
{fileName}
+ {size && manageByte(size)}
{onCheckedChange != null && }
>
@@ -179,6 +207,8 @@ const Browser = () => {
try {
const response = await axios.get('/api/browse', { params: { p: currentPath } });
setCurrentDirFiles(response.data);
+ let responseWithSize = await axios.get('/api/browse-withsize', { params: { p: currentPath } });
+ setCurrentDirFiles(responseWithSize.data);
} catch (err) {
console.error(err);
}
diff --git a/src/app.ts b/src/app.ts
index 82d6d6e..e73f124 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -228,6 +228,76 @@ export default ({ sharedPath: sharedPathIn, port, maxUploadSize, zipCompressionL
}));
+ async function getFolderSize(folderPath: string): Promise {
+ let totalSize = 0;
+
+ async function calculateSize(dirPath: string): Promise {
+ const files = await fs.readdir(dirPath); // Async readdir
+ for (const file of files) {
+ const filePath = join(dirPath, file);
+ const stats = await fs.stat(filePath); // Async stat
+
+ if (stats.isDirectory()) {
+ await calculateSize(filePath); // Recursively get size for subdirectories
+ } else {
+ totalSize += stats.size; // Add file size
+ }
+ }
+ }
+
+ await calculateSize(folderPath); // Start calculation from the root folder
+
+ return totalSize;
+ }
+
+ app.get('/api/browse-withsize', asyncHandler(async (req, res) => {
+ const browseRelPath = req.query['p'] || '/';
+ assert(typeof browseRelPath === 'string');
+ const browseAbsPath = await getFileAbsPath(browseRelPath);
+
+ let readdirEntries = await fs.readdir(browseAbsPath, { withFileTypes: true });
+ readdirEntries = readdirEntries.sort(({ name: a }, { name: b }) => new Intl.Collator(undefined, { numeric: true }).compare(a, b));
+
+ const entries = (await pMap(readdirEntries, async (entry) => {
+ try {
+ // TODO what if a file called ".."
+ const entryRelPath = join(browseRelPath, entry.name);
+ const entryAbsPath = join(browseAbsPath, entry.name);
+ const entryRealPath = await fs.realpath(entryAbsPath);
+
+ if (!entryRealPath.startsWith(sharedPath)) {
+ console.warn('Ignoring symlink pointing outside shared path', entryRealPath);
+ return [];
+ }
+
+ const stat = await fs.lstat(entryRealPath);
+ const isDir = stat.isDirectory();
+ const size = isDir ? await getFolderSize(entryRealPath) : stat.size
+
+ return [{
+ path: entryRelPath,
+ isDir,
+ fileName: entry.name,
+ size: size
+ }];
+ } catch (err) {
+ console.warn((err as Error).message);
+ // https://github.com/mifi/ezshare/issues/29
+ return [];
+ }
+ }, { concurrency: 10 })).flat();
+
+ res.send({
+ files: [
+ { path: join(browseRelPath, '..'), fileName: '..', isDir: true },
+ ...entries,
+ ],
+ cwd: browseRelPath,
+ sharedPath,
+ });
+ }));
+
+
app.get('/api/zip-files', asyncHandler(async (req, res) => {
const zipFileName = `${new Date().toISOString().replace(/^(\d+-\d+-\d+)T(\d+):(\d+):(\d+).*$/, '$1 $2.$3.$3')}.zip`;
const { files: filesJson } = req.query;