Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
"dependencies": {
"append-field": "^1.0.0",
"busboy": "^1.6.0",
"concat-stream": "^2.0.0",
"type-is": "^1.6.18"
},
"devDependencies": {
"concat-stream": "^2.0.0",
"deep-equal": "^2.0.3",
"express": "^4.21.2",
"form-data": "^4.0.2",
Expand Down
96 changes: 83 additions & 13 deletions storage/memory.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,91 @@
var concat = require('concat-stream')
const Writable = require('stream').Writable
const Buffer = require('buffer').Buffer
const isUint8Array = require('util').types.isUint8Array

function MemoryStorage (opts) {}
class MemoryStorage {
_handleFile (req, file, cb) {
file.stream.pipe(
new ConcatStream(function (data) {
cb(null, {
buffer: data,
size: data.length
})
})
)
}

MemoryStorage.prototype._handleFile = function _handleFile (req, file, cb) {
file.stream.pipe(concat({ encoding: 'buffer' }, function (data) {
cb(null, {
buffer: data,
size: data.length
_removeFile (req, file, cb) {
delete file.buffer
cb(null)
}
}

module.exports = function () {
return new MemoryStorage()
}

/**
* Writable stream that concatenates all written chunks into a single Buffer.
*
* Implementation inspired by the concat-stream npm package (https://www.npmjs.com/package/concat-stream),
* modified for our specific use case.
*/
class ConcatStream extends Writable {
/**
* Creates a new ConcatStream instance.
* @param {function(Buffer): void} cb - Callback invoked with the concatenated Buffer when the stream finishes.
*/
constructor (cb) {
super()
this.body = []
this.on('finish', function () {
Copy link

Copilot AI Aug 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback is invoked with this.getBody() but this context may not refer to the ConcatStream instance inside the 'finish' event handler. Use an arrow function or bind the context to ensure this refers to the ConcatStream instance.

Suggested change
this.on('finish', function () {
this.on('finish', () => {

Copilot uses AI. Check for mistakes.

cb(this.getBody())
})
}))
}

_write (chunk, enc, next) {
this.body.push(chunk)
next()
}

/**
* Concatenates all collected chunks into a single Buffer.
* @returns {Buffer} The concatenated buffer containing all written data.
*/
getBody () {
const bufs = []
for (const p of this.body) {
// Buffer.concat can handle Buffer and Uint8Array (and subclasses)
if (Buffer.isBuffer(p) || isUint8Array(p)) {
bufs.push(p)
} else if (isBufferish(p)) {
bufs.push(Buffer.from(p))
} else {
bufs.push(Buffer.from(String(p)))
}
}
return Buffer.concat(bufs)
}
}

MemoryStorage.prototype._removeFile = function _removeFile (req, file, cb) {
delete file.buffer
cb(null)
/**
* Checks if the given value is array-like.
* @param {*} arr
* @returns {boolean}
*/
function isArrayish (arr) {
return /Array\]$/.test(Object.prototype.toString.call(arr))
}

module.exports = function (opts) {
return new MemoryStorage(opts)
/**
* Checks if the given value is Buffer-like.
* @param {*} p
* @returns {boolean}
*/
function isBufferish (p) {
return (
typeof p === 'string' ||
isArrayish(p) ||
(p && typeof p.subarray === 'function')
)
}