Delete unmoved upload temp files when the response closes

This commit is contained in:
mikiher committed 2026-10-31 23:18:01 +02:00
1 parent edb1f7efc9
commit 48d9309eb2
2 files changed
+39 -5

No files matched your search

@@ -26,6 +26,18 @@ const waitFlushProperty = Symbol('wait flush property symbol');
*/
module.exports = (options, req, res, next) => {
req.files = null;
const pendingCleanups = [];
const runPendingCleanups = () => {
while (pendingCleanups.length) {
const cleanupFn = pendingCleanups.pop();
try {
cleanupFn();
} catch (err) {
debugLog(options, `Temp file cleanup failed: ${err}`);
}
}
};
res.once('close', runPendingCleanups);
// Build busboy options and init busboy instance.
const busboyOptions = buildOptions(options, { headers: req.headers });
@@ -62,6 +74,7 @@ module.exports = (options, req, res, next) => {
} = options.useTempFiles
? tempFileHandler(options, field, filename) // Upload into temporary file.
: memHandler(options, field, filename); // Upload into RAM.
pendingCleanups.push(cleanup);
const writePromise = options.useTempFiles
? getWritePromise().catch(err => {
@@ -161,6 +174,7 @@ module.exports = (options, req, res, next) => {
busboy.on('error', (err) => {
debugLog(options, `Busboy error`);
runPendingCleanups();
next(err);
});
@@ -28,6 +28,18 @@ module.exports = (options, fieldname, filename) => {
reject(err);
});
});
// cleanup() can run from an error path and again on response close.
let cleanedUp = false;
// Windows cannot unlink while the write stream still holds the file.
const closeWriteStream = () => new Promise((resolve) => {
if (writeStream.destroyed || writeStream.closed) {
return resolve();
}
writeStream.once('close', resolve);
writeStream.once('error', resolve);
writeStream.end();
});
return {
dataHandler: (data) => {
@@ -51,13 +63,21 @@ module.exports = (options, fieldname, filename) => {
return Buffer.concat([]);
},
cleanup: () => {
if (cleanedUp) return Promise.resolve();
cleanedUp = true;
completed = true;
debugLog(options, `Cleaning up temporary file ${tempFilePath}...`);
writeStream.end();
deleteFile(tempFilePath, err => (err
? debugLog(options, `Cleaning up temporary file ${tempFilePath} failed: ${err}`)
: debugLog(options, `Cleaning up temporary file ${tempFilePath} done.`)
));
return closeWriteStream().then(() => new Promise((resolve) => {
// ENOENT: handler already mv()'d the file; that is success.
deleteFile(tempFilePath, err => {
if (err && err.code !== 'ENOENT') {
debugLog(options, `Cleaning up temporary file ${tempFilePath} failed: ${err}`);
} else {
debugLog(options, `Cleaning up temporary file ${tempFilePath} done.`);
}
resolve();
});
}));
},
getWritePromise: () => writePromise
};