fix: extreme PDF compression producing empty output on large files

The multipart body parser was using the first \r\n\r\n as the file
content start, but this could miss the actual file part. Now properly
iterates through parts to find the one with filename= header, and
uses lastIndexOf for the closing boundary to avoid false matches
inside PDF binary data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-11 14:48:36 +02:00
parent d7bd1a7f5d
commit 2739c6af6f
+52 -15
View File
@@ -120,18 +120,16 @@ export async function POST(req: NextRequest) {
const nodeStream = Readable.fromWeb(req.body as import("stream/web").ReadableStream);
await pipeline(nodeStream, createWriteStream(rawPath));
// Extract the PDF from multipart: find the double CRLF after headers,
// then read until the boundary marker before the end
// Extract the PDF binary from the multipart body.
// Multipart format:
// --boundary\r\n
// Content-Disposition: form-data; name="fileInput"; filename="x.pdf"\r\n
// Content-Type: application/pdf\r\n
// \r\n
// <FILE BYTES>
// \r\n--boundary--\r\n
const rawBuf = await readFile(rawPath);
const headerEnd = rawBuf.indexOf(Buffer.from("\r\n\r\n"));
if (headerEnd === -1) {
return NextResponse.json(
{ error: "Lipsește fișierul PDF." },
{ status: 400 },
);
}
// Extract boundary from Content-Type header
const contentType = req.headers.get("content-type") || "";
const boundaryMatch = contentType.match(/boundary=(?:"([^"]+)"|([^\s;]+))/);
const boundary = boundaryMatch?.[1] ?? boundaryMatch?.[2] ?? "";
@@ -143,11 +141,50 @@ export async function POST(req: NextRequest) {
);
}
// File content starts after first double CRLF, ends before closing boundary
const closingBoundary = Buffer.from(`\r\n--${boundary}`);
const fileStart = headerEnd + 4;
const fileEnd = rawBuf.indexOf(closingBoundary, fileStart);
const pdfData = fileEnd !== -1 ? rawBuf.subarray(fileStart, fileEnd) : rawBuf.subarray(fileStart);
const boundaryBuf = Buffer.from(`--${boundary}`);
// Find the part that contains a filename (the file upload part).
// There may be multiple parts — we need the one with "filename=".
let fileStart = -1;
let searchFrom = 0;
while (searchFrom < rawBuf.length) {
const partStart = rawBuf.indexOf(boundaryBuf, searchFrom);
if (partStart === -1) break;
// Skip past boundary line to get to headers
const headersStart = rawBuf.indexOf(Buffer.from("\r\n"), partStart);
if (headersStart === -1) break;
const headerEnd = rawBuf.indexOf(Buffer.from("\r\n\r\n"), headersStart);
if (headerEnd === -1) break;
// Check if this part's headers contain a filename
const headers = rawBuf.subarray(headersStart, headerEnd).toString("utf8");
if (headers.includes("filename=")) {
fileStart = headerEnd + 4; // skip \r\n\r\n
break;
}
// Move past this part's headers to search for next boundary
searchFrom = headerEnd + 4;
}
if (fileStart === -1) {
return NextResponse.json(
{ error: "Lipsește fișierul PDF." },
{ status: 400 },
);
}
// Find the closing boundary after the file content.
// Search from the END of the buffer backwards, since the PDF binary
// could theoretically contain the boundary string by coincidence.
const closingMarker = Buffer.from(`\r\n--${boundary}`);
const fileEnd = rawBuf.lastIndexOf(closingMarker);
const pdfData = (fileEnd > fileStart)
? rawBuf.subarray(fileStart, fileEnd)
: rawBuf.subarray(fileStart);
await writeFile(inputPath, pdfData);
const originalSize = pdfData.length;