Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for reading from Blob in Node.js #588

Merged
merged 3 commits into from Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.
Borewit marked this conversation as resolved.
Show resolved Hide resolved

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