Skip to content

Commit 3217da9

Browse files
authored
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
1 parent ae6b565 commit 3217da9

File tree

5 files changed

+98
-1
lines changed

5 files changed

+98
-1
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ jobs:
4747
node-version: ${{steps.get-version.outputs.node}}
4848

4949
- run: npm install
50+
- run: npm install domexception
5051

5152
- run: npm run report -- --colors
5253

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ fetch('https://httpbin.org/post', {
2727
.then(json => console.log(json));
2828
```
2929

30+
### Blob part backed up by filesystem
31+
To use, install [domexception](https://github.com/jsdom/domexception).
32+
33+
```sh
34+
npm install fetch-blob domexception
35+
```
36+
37+
```js
38+
const blobFrom = require('fetch-blob/from.js');
39+
const blob1 = blobFrom('./2-GiB-file.bin');
40+
const blob2 = blobFrom('./2-GiB-file.bin');
41+
42+
// Not a 4 GiB memory snapshot, just holds 3 references
43+
// points to where data is located on the disk
44+
const blob = new Blob([blob1, blob2]);
45+
console.log(blob.size) // 4 GiB
46+
```
47+
3048
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.
3149

3250
[npm-image]: https://flat.badgen.net/npm/v/fetch-blob

from.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const {statSync, createReadStream} = require('fs');
2+
const Blob = require('.');
3+
const DOMException = require('domexception');
4+
5+
/**
6+
* @param {string} path filepath on the disk
7+
* @returns {Blob}
8+
*/
9+
function blobFrom(path) {
10+
const {size, mtime} = statSync(path);
11+
const blob = new BlobDataItem({path, size, mtime});
12+
13+
return new Blob([blob]);
14+
}
15+
16+
/**
17+
* This is a blob backed up by a file on the disk
18+
* with minium requirement
19+
*
20+
* @private
21+
*/
22+
class BlobDataItem {
23+
constructor(options) {
24+
this.size = options.size;
25+
this.path = options.path;
26+
this.start = options.start;
27+
this.mtime = options.mtime;
28+
}
29+
30+
// Slicing arguments is validated and formated
31+
// by Blob.prototype.slice
32+
slice(start, end) {
33+
return new BlobDataItem({
34+
path: this.path,
35+
start,
36+
mtime: this.mtime,
37+
size: end - start
38+
});
39+
}
40+
41+
stream() {
42+
if (statSync(this.path).mtime > this.mtime) {
43+
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');
44+
}
45+
46+
return createReadStream(this.path, {
47+
start: this.start,
48+
end: this.start + this.size - 1
49+
});
50+
}
51+
52+
get [Symbol.toStringTag]() {
53+
return 'Blob';
54+
}
55+
}
56+
57+
module.exports = blobFrom;

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "A Blob implementation in Node.js, originally from node-fetch.",
55
"main": "index.js",
66
"files": [
7+
"from.js",
78
"index.js",
89
"index.d.ts"
910
],
@@ -48,5 +49,7 @@
4849
}
4950
]
5051
},
51-
"dependencies": {}
52+
"peerDependencies": {
53+
"domexception": "^2.0.1"
54+
}
5255
}

test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
const fs = require('fs');
12
const test = require('ava');
23
const Blob = require('.');
4+
const blobFrom = require('./from');
35
const getStream = require('get-stream');
46
const {Response} = require('node-fetch');
57
const {TextDecoder} = require('util');
@@ -142,3 +144,19 @@ test('Blob works with node-fetch Response.text()', async t => {
142144
const text = await response.text();
143145
t.is(text, data);
144146
});
147+
148+
test('blob part backed up by filesystem', async t => {
149+
const blob = blobFrom('./LICENSE');
150+
t.is(await blob.slice(0, 3).text(), 'MIT');
151+
t.is(await blob.slice(4, 11).text(), 'License');
152+
});
153+
154+
test('Reading after modified should fail', async t => {
155+
const blob = blobFrom('./LICENSE');
156+
await new Promise(resolve => setTimeout(resolve, 100));
157+
const now = new Date();
158+
// Change modified time
159+
fs.utimesSync('./LICENSE', now, now);
160+
const error = await blob.text().catch(error => error);
161+
t.is(error.name, 'NotReadableError');
162+
});

0 commit comments

Comments
 (0)