Skip to content

Commit 5c8db99

Browse files
SashaMITcursoragent
andcommitted
fix: large file uploads OOM on memory-constrained devices (Jetson)
Switch multer from memoryStorage to diskStorage for the /batch upload endpoint. Previously, the entire file was buffered in RAM during upload, causing Node.js to OOM or crash at ~50% on a 100MB file on Jetson. Now uploads stream to a temp directory on disk. The file is read into memory only briefly when passing to IPFS for CID generation. Temp files are cleaned up after processing. Also bumps max file size from 100MB to 500MB. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 768d550 commit 5c8db99

2 files changed

Lines changed: 32 additions & 6 deletions

File tree

pc2-node/src/api/index.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import express, { Express, Request, Response, NextFunction } from 'express';
22
import cookieParser from 'cookie-parser';
33
import multer from 'multer';
4+
import os from 'os';
5+
import path from 'path';
6+
import fs from 'fs';
47
import { DatabaseManager, FilesystemManager } from '../storage/index.js';
58
import { Config } from '../config/loader.js';
69
import { Server as SocketIOServer } from 'socket.io';
@@ -395,11 +398,22 @@ export function setupAPI(app: Express): void {
395398
app.get('/df', authenticate, handleDF);
396399
app.post('/df', authenticate, handleDF);
397400

398-
// Batch endpoint with multer for multipart file uploads
401+
// Batch endpoint with multer for multipart file uploads.
402+
// Uses diskStorage to stream uploads to a temp dir instead of buffering
403+
// entirely in RAM -- critical for large files on memory-constrained devices.
404+
const uploadTmpDir = path.join(os.tmpdir(), 'pc2-uploads');
405+
if (!fs.existsSync(uploadTmpDir)) fs.mkdirSync(uploadTmpDir, { recursive: true });
406+
399407
const upload = multer({
400-
storage: multer.memoryStorage(),
408+
storage: multer.diskStorage({
409+
destination: uploadTmpDir,
410+
filename: (_req, file, cb) => {
411+
const unique = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
412+
cb(null, `${unique}-${file.originalname}`);
413+
},
414+
}),
401415
limits: {
402-
fileSize: 100 * 1024 * 1024 // 100MB max file size
416+
fileSize: 500 * 1024 * 1024 // 500MB max file size
403417
}
404418
});
405419

pc2-node/src/api/info.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,13 @@ export async function handleBatch(req: AuthenticatedRequest, res: Response): Pro
446446
// Normalize the path (remove double slashes, etc.)
447447
filePath = filePath.replace(/\/+/g, '/');
448448

449-
// Get file content - prioritize buffer, fallback to data
450-
const fileContent = file.buffer || file.data;
449+
// Get file content - buffer (memoryStorage), data, or read from disk (diskStorage)
450+
let fileContent = file.buffer || file.data;
451+
if (!fileContent && file.path) {
452+
const { readFileSync, unlinkSync } = await import('fs');
453+
fileContent = readFileSync(file.path);
454+
try { unlinkSync(file.path); } catch { /* cleanup best-effort */ }
455+
}
451456
const reportedSize = file.size || (fileContent ? fileContent.length : 0);
452457
const actualSize = fileContent ? (Buffer.isBuffer(fileContent) ? fileContent.length : (fileContent instanceof Uint8Array ? fileContent.length : Buffer.byteLength(fileContent))) : 0;
453458

@@ -578,9 +583,16 @@ export async function handleBatch(req: AuthenticatedRequest, res: Response): Pro
578583
filePath
579584
});
580585

586+
let singleFileContent = file.buffer || file.data;
587+
if (!singleFileContent && file.path) {
588+
const { readFileSync, unlinkSync } = await import('fs');
589+
singleFileContent = readFileSync(file.path);
590+
try { unlinkSync(file.path); } catch { /* cleanup best-effort */ }
591+
}
592+
581593
const metadata = await filesystem.writeFile(
582594
filePath,
583-
file.buffer || file.data,
595+
singleFileContent,
584596
req.user.wallet_address,
585597
{
586598
mimeType: file.mimetype || file.type

0 commit comments

Comments
 (0)