Create blob backed up by filesystem (#55)
* create a blob backed up by filesystem * added code coverage test for filesystem blobs * updated readme for using filesystem * install peer dep * added from to files array in package.json
This commit is contained in:
@@ -47,6 +47,7 @@ jobs:
|
||||
node-version: ${{steps.get-version.outputs.node}}
|
||||
|
||||
- run: npm install
|
||||
- run: npm install domexception
|
||||
|
||||
- run: npm run report -- --colors
|
||||
|
||||
|
||||
@@ -27,6 +27,24 @@ fetch('https://httpbin.org/post', {
|
||||
.then(json => console.log(json));
|
||||
```
|
||||
|
||||
### Blob part backed up by filesystem
|
||||
To use, install [domexception](https://github.com/jsdom/domexception).
|
||||
|
||||
```sh
|
||||
npm install fetch-blob domexception
|
||||
```
|
||||
|
||||
```js
|
||||
const blobFrom = require('fetch-blob/from.js');
|
||||
const blob1 = blobFrom('./2-GiB-file.bin');
|
||||
const blob2 = blobFrom('./2-GiB-file.bin');
|
||||
|
||||
// Not a 4 GiB memory snapshot, just holds 3 references
|
||||
// points to where data is located on the disk
|
||||
const blob = new Blob([blob1, blob2]);
|
||||
console.log(blob.size) // 4 GiB
|
||||
```
|
||||
|
||||
See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [tests](https://github.com/node-fetch/fetch-blob/blob/master/test.js) for more details.
|
||||
|
||||
[npm-image]: https://flat.badgen.net/npm/v/fetch-blob
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
const {statSync, createReadStream} = require('fs');
|
||||
const Blob = require('.');
|
||||
const DOMException = require('domexception');
|
||||
|
||||
/**
|
||||
* @param {string} path filepath on the disk
|
||||
* @returns {Blob}
|
||||
*/
|
||||
function blobFrom(path) {
|
||||
const {size, mtime} = statSync(path);
|
||||
const blob = new BlobDataItem({path, size, mtime});
|
||||
|
||||
return new Blob([blob]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a blob backed up by a file on the disk
|
||||
* with minium requirement
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
class BlobDataItem {
|
||||
constructor(options) {
|
||||
this.size = options.size;
|
||||
this.path = options.path;
|
||||
this.start = options.start;
|
||||
this.mtime = options.mtime;
|
||||
}
|
||||
|
||||
// Slicing arguments is validated and formated
|
||||
// by Blob.prototype.slice
|
||||
slice(start, end) {
|
||||
return new BlobDataItem({
|
||||
path: this.path,
|
||||
start,
|
||||
mtime: this.mtime,
|
||||
size: end - start
|
||||
});
|
||||
}
|
||||
|
||||
stream() {
|
||||
if (statSync(this.path).mtime > this.mtime) {
|
||||
throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError');
|
||||
}
|
||||
|
||||
return createReadStream(this.path, {
|
||||
start: this.start,
|
||||
end: this.start + this.size - 1
|
||||
});
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return 'Blob';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = blobFrom;
|
||||
+4
-1
@@ -4,6 +4,7 @@
|
||||
"description": "A Blob implementation in Node.js, originally from node-fetch.",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"from.js",
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
@@ -48,5 +49,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"dependencies": {}
|
||||
"peerDependencies": {
|
||||
"domexception": "^2.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const fs = require('fs');
|
||||
const test = require('ava');
|
||||
const Blob = require('.');
|
||||
const blobFrom = require('./from');
|
||||
const getStream = require('get-stream');
|
||||
const {Response} = require('node-fetch');
|
||||
const {TextDecoder} = require('util');
|
||||
@@ -142,3 +144,19 @@ test('Blob works with node-fetch Response.text()', async t => {
|
||||
const text = await response.text();
|
||||
t.is(text, data);
|
||||
});
|
||||
|
||||
test('blob part backed up by filesystem', async t => {
|
||||
const blob = blobFrom('./LICENSE');
|
||||
t.is(await blob.slice(0, 3).text(), 'MIT');
|
||||
t.is(await blob.slice(4, 11).text(), 'License');
|
||||
});
|
||||
|
||||
test('Reading after modified should fail', async t => {
|
||||
const blob = blobFrom('./LICENSE');
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
const now = new Date();
|
||||
// Change modified time
|
||||
fs.utimesSync('./LICENSE', now, now);
|
||||
const error = await blob.text().catch(error => error);
|
||||
t.is(error.name, 'NotReadableError');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user