Skip to content

Commit

Permalink
Support reading from Blob in Node.js (#588)
Browse files Browse the repository at this point in the history
  • Loading branch information
Borewit committed Mar 31, 2023
1 parent b89702c commit 1c75cfb
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 61 deletions.
21 changes: 1 addition & 20 deletions browser.d.ts
Expand Up @@ -18,28 +18,9 @@ console.log(fileType);
*/
export declare function fileTypeFromStream(stream: ReadableStream): Promise<FileTypeResult | undefined>;

/**
Detect the file type of a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
__Note:__ This method is only available in the browser.
@example
```
import {fileTypeFromBlob} from 'file-type';
const blob = new Blob(['<?xml version="1.0" encoding="ISO-8859-1" ?>'], {
type: 'plain/text',
endings: 'native'
});
console.log(await fileTypeFromBlob(blob));
//=> {ext: 'txt', mime: 'plain/text'}
```
*/
export declare function fileTypeFromBlob(blob: Blob): Promise<FileTypeResult | undefined>;

export {
fileTypeFromBuffer,
fileTypeFromBlob,
supportedExtensions,
supportedMimeTypes,
type FileTypeResult,
Expand Down
38 changes: 1 addition & 37 deletions browser.js
@@ -1,36 +1,5 @@
import {Buffer} from 'node:buffer';
import {ReadableWebToNodeStream} from 'readable-web-to-node-stream';
import {fileTypeFromBuffer, fileTypeFromStream as coreFileTypeFromStream} from './core.js';

/**
Convert Blobs to ArrayBuffer.
@param {Blob} blob - Web API Blob.
@returns {Promise<ArrayBuffer>}
*/
function blobToArrayBuffer(blob) {
if (blob.arrayBuffer) {
return blob.arrayBuffer();
}

// TODO: Remove when stop supporting older environments
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.addEventListener('loadend', event => {
resolve(event.target.result);
});

fileReader.addEventListener('error', event => {
reject(new Error(event.message));
});

fileReader.addEventListener('abort', event => {
reject(new Error(event.type));
});

fileReader.readAsArrayBuffer(blob);
});
}
import {fileTypeFromStream as coreFileTypeFromStream} from './core.js';

export async function fileTypeFromStream(stream) {
const readableWebToNodeStream = new ReadableWebToNodeStream(stream);
Expand All @@ -39,11 +8,6 @@ export async function fileTypeFromStream(stream) {
return fileType;
}

export async function fileTypeFromBlob(blob) {
const buffer = await blobToArrayBuffer(blob);
return fileTypeFromBuffer(Buffer.from(buffer));
}

export {
fileTypeFromTokenizer,
fileTypeFromBuffer,
Expand Down
18 changes: 18 additions & 0 deletions core.d.ts
Expand Up @@ -401,3 +401,21 @@ if (stream2.fileType?.mime === 'image/jpeg') {
```
*/
export function fileTypeStream(readableStream: ReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;

/**
Detect the file type of a [`Blob`](https://nodejs.org/api/buffer.html#class-blob).
@example
```
import {fileTypeFromBlob} from 'file-type';
const blob = new Blob(['<?xml version="1.0" encoding="ISO-8859-1" ?>'], {
type: 'plain/text',
endings: 'native'
});
console.log(await fileTypeFromBlob(blob));
//=> {ext: 'txt', mime: 'plain/text'}
```
*/
export declare function fileTypeFromBlob(blob: Blob): Promise<FileTypeResult | undefined>;
5 changes: 5 additions & 0 deletions core.js
Expand Up @@ -33,6 +33,11 @@ export async function fileTypeFromBuffer(input) {
return fileTypeFromTokenizer(strtok3.fromBuffer(buffer));
}

export async function fileTypeFromBlob(blob) {
const buffer = await blob.arrayBuffer();
return fileTypeFromBuffer(new Uint8Array(buffer));
}

function _check(buffer, headers, options) {
options = {
offset: 0,
Expand Down
2 changes: 1 addition & 1 deletion index.test-d.ts
Expand Up @@ -2,10 +2,10 @@ import {Buffer} from 'node:buffer';
import {createReadStream} from 'node:fs';
import {expectType} from 'tsd';
import {
fileTypeFromBlob,
type FileTypeResult as FileTypeResultBrowser,
} from './browser.js';
import {
fileTypeFromBlob,
fileTypeFromBuffer,
fileTypeFromFile,
fileTypeFromStream,
Expand Down
2 changes: 0 additions & 2 deletions readme.md
Expand Up @@ -198,8 +198,6 @@ A readable stream representing file data.

Detect the file type of a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).

**Note:** This method is only available in the browser.

The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.

Returns a `Promise` for an object with the detected file type and MIME type:
Expand Down
19 changes: 18 additions & 1 deletion test.js
@@ -1,5 +1,5 @@
import process from 'node:process';
import {Buffer} from 'node:buffer';
import {Buffer, Blob} from 'node:buffer';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import fs from 'node:fs';
Expand All @@ -11,6 +11,7 @@ import {
fileTypeFromBuffer,
fileTypeFromStream,
fileTypeFromFile,
fileTypeFromBlob,
fileTypeStream,
supportedExtensions,
supportedMimeTypes,
Expand Down Expand Up @@ -262,6 +263,13 @@ async function checkBufferLike(t, type, bufferLike) {
t.is(typeof mime, 'string');
}

async function checkBlobLike(t, type, bufferLike) {
const blob = new Blob([bufferLike]);
const {ext, mime} = await fileTypeFromBlob(blob) ?? {};
t.is(ext, type);
t.is(typeof mime, 'string');
}

async function checkFile(t, type, filePath) {
const {ext, mime} = await fileTypeFromFile(filePath) ?? {};
t.is(ext, type);
Expand All @@ -283,6 +291,14 @@ async function testFromBuffer(t, ext, name) {
await checkBufferLike(t, ext, chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength));
}

async function testFromBlob(t, ext, name) {
const fixtureName = `${(name ?? 'fixture')}.${ext}`;

const file = path.join(__dirname, 'fixture', fixtureName);
const chunk = fs.readFileSync(file);
await checkBlobLike(t, ext, chunk);
}

async function testFalsePositive(t, ext, name) {
const file = path.join(__dirname, 'fixture', `${name}.${ext}`);

Expand Down Expand Up @@ -334,6 +350,7 @@ for (const type of types) {

_test(`${name}.${type} ${i++} .fileTypeFromFile() method - same fileType`, testFromFile, type, name);
_test(`${name}.${type} ${i++} .fileTypeFromBuffer() method - same fileType`, testFromBuffer, type, name);
_test(`${name}.${type} ${i++} .fileTypeFromBlob() method - same fileType`, testFromBlob, type, name);
_test(`${name}.${type} ${i++} .fileTypeFromStream() method - same fileType`, testFileFromStream, type, name);
test(`${name}.${type} ${i++} .fileTypeStream() - identical streams`, testStream, type, name);
}
Expand Down

0 comments on commit 1c75cfb

Please sign in to comment.