|
1 | | -var concat = require('concat-stream') |
| 1 | +const Writable = require("stream").Writable; |
| 2 | +const Buffer = require("buffer").Buffer; |
| 3 | +const isUint8Array = require('util').types.isUint8Array; |
2 | 4 |
|
3 | | -function MemoryStorage (opts) {} |
| 5 | +class MemoryStorage { |
| 6 | + _handleFile(req, file, cb) { |
| 7 | + file.stream.pipe( |
| 8 | + new ConcatStream(function (data) { |
| 9 | + cb(null, { |
| 10 | + buffer: data, |
| 11 | + size: data.length, |
| 12 | + }); |
| 13 | + }) |
| 14 | + ); |
| 15 | + } |
4 | 16 |
|
5 | | -MemoryStorage.prototype._handleFile = function _handleFile (req, file, cb) { |
6 | | - file.stream.pipe(concat({ encoding: 'buffer' }, function (data) { |
7 | | - cb(null, { |
8 | | - buffer: data, |
9 | | - size: data.length |
10 | | - }) |
11 | | - })) |
| 17 | + _removeFile(req, file, cb) { |
| 18 | + delete file.buffer; |
| 19 | + cb(null); |
| 20 | + } |
12 | 21 | } |
13 | 22 |
|
14 | | -MemoryStorage.prototype._removeFile = function _removeFile (req, file, cb) { |
15 | | - delete file.buffer |
16 | | - cb(null) |
| 23 | +module.exports = function () { |
| 24 | + return new MemoryStorage(); |
| 25 | +}; |
| 26 | + |
| 27 | +/** |
| 28 | + * Writable stream that concatenates all written chunks into a single Buffer. |
| 29 | + * |
| 30 | + * Implementation inspired by the concat-stream npm package (https://www.npmjs.com/package/concat-stream), |
| 31 | + * modified for our specific use case. |
| 32 | + */ |
| 33 | +class ConcatStream extends Writable { |
| 34 | + /** |
| 35 | + * Creates a new ConcatStream instance. |
| 36 | + * @param {function(Buffer): void} cb - Callback invoked with the concatenated Buffer when the stream finishes. |
| 37 | + */ |
| 38 | + constructor(cb) { |
| 39 | + super(); |
| 40 | + this.body = []; |
| 41 | + this.on("finish", function () { |
| 42 | + cb(this.getBody()); |
| 43 | + }); |
| 44 | + } |
| 45 | + |
| 46 | + _write(chunk, enc, next) { |
| 47 | + this.body.push(chunk); |
| 48 | + next(); |
| 49 | + } |
| 50 | + |
| 51 | + /** |
| 52 | + * Concatenates all collected chunks into a single Buffer. |
| 53 | + * @returns {Buffer} The concatenated buffer containing all written data. |
| 54 | + */ |
| 55 | + getBody() { |
| 56 | + const bufs = []; |
| 57 | + for (const p of this.body) { |
| 58 | + // Buffer.concat can handle Buffer and Uint8Array (and subclasses) |
| 59 | + if (Buffer.isBuffer(p) || isUint8Array(p)) { |
| 60 | + bufs.push(p); |
| 61 | + } else if (isBufferish(p)) { |
| 62 | + bufs.push(Buffer.from(p)); |
| 63 | + } else { |
| 64 | + bufs.push(Buffer.from(String(p))); |
| 65 | + } |
| 66 | + } |
| 67 | + return Buffer.concat(bufs); |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +/** |
| 72 | + * Checks if the given value is array-like. |
| 73 | + * @param {*} arr |
| 74 | + * @returns {boolean} |
| 75 | + */ |
| 76 | +function isArrayish(arr) { |
| 77 | + return /Array\]$/.test(Object.prototype.toString.call(arr)); |
17 | 78 | } |
18 | 79 |
|
19 | | -module.exports = function (opts) { |
20 | | - return new MemoryStorage(opts) |
| 80 | +/** |
| 81 | + * Checks if the given value is Buffer-like. |
| 82 | + * @param {*} p |
| 83 | + * @returns {boolean} |
| 84 | + */ |
| 85 | +function isBufferish(p) { |
| 86 | + return ( |
| 87 | + typeof p === "string" || |
| 88 | + isArrayish(p) || |
| 89 | + (p && typeof p.subarray === "function") |
| 90 | + ); |
21 | 91 | } |
0 commit comments