b2b-platform-utils 1.1.26 → 1.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fileSystem.js +114 -0
- package/package.json +5 -1
package/fileSystem.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const http = require("http");
|
|
4
|
+
const https = require("https");
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* @module fileSystem
|
|
5
8
|
* @description
|
|
@@ -299,6 +302,116 @@ function getAbsoluteMediaResourceUrl(service, instance, fileName) {
|
|
|
299
302
|
return `${baseUrl}/file/${service}/${instance}/${fileName}`;
|
|
300
303
|
}
|
|
301
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Download remote resource to Buffer or read local file to Buffer.
|
|
307
|
+
*
|
|
308
|
+
* @param {string} source - URL (http/https) or local file path.
|
|
309
|
+
* @param {Object} [options]
|
|
310
|
+
* @param {number} [options.timeoutMs=15000] - Request timeout.
|
|
311
|
+
* @param {number} [options.maxBytes=26214400] - Max allowed size in bytes (default 25MB).
|
|
312
|
+
* @param {number} [options.maxRedirects=3] - Max redirects to follow.
|
|
313
|
+
* @returns {Promise<Buffer>}
|
|
314
|
+
*/
|
|
315
|
+
function downloadToBuffer(source, options = {}) {
|
|
316
|
+
const timeoutMs = Number.isInteger(options.timeoutMs)
|
|
317
|
+
? options.timeoutMs
|
|
318
|
+
: 15000;
|
|
319
|
+
const maxBytes = Number.isInteger(options.maxBytes)
|
|
320
|
+
? options.maxBytes
|
|
321
|
+
: 25 * 1024 * 1024;
|
|
322
|
+
const maxRedirects = Number.isInteger(options.maxRedirects)
|
|
323
|
+
? options.maxRedirects
|
|
324
|
+
: 3;
|
|
325
|
+
|
|
326
|
+
if (!source || typeof source !== "string") {
|
|
327
|
+
return Promise.reject(
|
|
328
|
+
new TypeError("downloadToBuffer: source must be a non-empty string")
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const trimmed = source.trim();
|
|
333
|
+
|
|
334
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
335
|
+
return downloadHttpToBuffer(trimmed, { timeoutMs, maxBytes, maxRedirects });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const absPath = resolvePath(trimmed);
|
|
339
|
+
return fsPromises.readFile(absPath);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Download an HTTP/HTTPS resource to Buffer with basic redirect support.
|
|
344
|
+
*
|
|
345
|
+
* @param {string} url
|
|
346
|
+
* @param {Object} options
|
|
347
|
+
* @param {number} options.timeoutMs
|
|
348
|
+
* @param {number} options.maxBytes
|
|
349
|
+
* @param {number} options.maxRedirects
|
|
350
|
+
* @returns {Promise<Buffer>}
|
|
351
|
+
*/
|
|
352
|
+
function downloadHttpToBuffer(url, options) {
|
|
353
|
+
return new Promise((resolve, reject) => {
|
|
354
|
+
const client = url.startsWith("https://") ? https : http;
|
|
355
|
+
|
|
356
|
+
const req = client.get(url, (res) => {
|
|
357
|
+
const statusCode = res.statusCode || 0;
|
|
358
|
+
|
|
359
|
+
// Follow redirects
|
|
360
|
+
if (
|
|
361
|
+
statusCode >= 300 &&
|
|
362
|
+
statusCode < 400 &&
|
|
363
|
+
res.headers.location &&
|
|
364
|
+
options.maxRedirects > 0
|
|
365
|
+
) {
|
|
366
|
+
res.resume();
|
|
367
|
+
const nextUrl = new URL(res.headers.location, url).toString();
|
|
368
|
+
return resolve(
|
|
369
|
+
downloadHttpToBuffer(nextUrl, {
|
|
370
|
+
...options,
|
|
371
|
+
maxRedirects: options.maxRedirects - 1,
|
|
372
|
+
})
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
377
|
+
res.resume();
|
|
378
|
+
return reject(
|
|
379
|
+
new Error(
|
|
380
|
+
`downloadToBuffer: request failed with status ${statusCode}`
|
|
381
|
+
)
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const chunks = [];
|
|
386
|
+
let received = 0;
|
|
387
|
+
|
|
388
|
+
res.on("data", (chunk) => {
|
|
389
|
+
received += chunk.length;
|
|
390
|
+
if (received > options.maxBytes) {
|
|
391
|
+
req.destroy(
|
|
392
|
+
new Error(
|
|
393
|
+
`downloadToBuffer: maxBytes exceeded (${options.maxBytes})`
|
|
394
|
+
)
|
|
395
|
+
);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
chunks.push(chunk);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
402
|
+
res.on("error", reject);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
req.setTimeout(options.timeoutMs, () => {
|
|
406
|
+
req.destroy(
|
|
407
|
+
new Error(`downloadToBuffer: timeout after ${options.timeoutMs}ms`)
|
|
408
|
+
);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
req.on("error", reject);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
302
415
|
// -----------------------------------------------------------------------------
|
|
303
416
|
// Exports
|
|
304
417
|
// -----------------------------------------------------------------------------
|
|
@@ -311,6 +424,7 @@ module.exports = {
|
|
|
311
424
|
readJsonFile,
|
|
312
425
|
removeFile,
|
|
313
426
|
unlinkIfExists,
|
|
427
|
+
downloadToBuffer,
|
|
314
428
|
|
|
315
429
|
// MIME helpers
|
|
316
430
|
getImageExtension,
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "b2b-platform-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.28",
|
|
4
4
|
"description": "Shared utilities for Node.js microservices: errors map, local cache, logger, numbers, dates, filesystem, media optimization, paginator, slugger, crypto wrapper, sanitize HTML, sorting.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "KingSizer",
|
|
7
7
|
"private": false,
|
|
8
|
+
"scripts": {
|
|
9
|
+
"publish-update": "npm version patch --no-git-tag-version && npm publish",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
8
12
|
"files": [
|
|
9
13
|
"*.js",
|
|
10
14
|
"README.md"
|