@lyleunderwood/filereader-polyfill
Version:
W3C File API specification compliant FileReader polyfill for Node.js environments
322 lines (236 loc) • 9.4 kB
Markdown
# FileReader Polyfill
A comprehensive FileReader API polyfill for Node.js environments that provides **100% W3C File API specification compliance** with full compatibility with the Web FileReader API, including support for both File and Blob objects.
## Features
- ✅ **W3C File API Specification Compliant** - Passes comprehensive spec compliance tests
- ✅ Complete FileReader API implementation
- ✅ Support for all read methods: `readAsText`, `readAsDataURL`, `readAsArrayBuffer`, `readAsBinaryString`
- ✅ Full event support: `loadstart`, `progress`, `load`, `error`, `abort`, `loadend`
- ✅ Proper error handling with DOMException types (`InvalidStateError`, `NotReadableError`, `AbortError`)
- ✅ Works with both File and Blob objects
- ✅ Stream-based reading with progress events
- ✅ Proper abort functionality
- ✅ File path support for Node.js environments
- ✅ TypeScript support with full type definitions
- ✅ Event-driven architecture with EventTarget inheritance
## Requirements
- Node.js 16.7.0 or higher (required for native `Blob` and `EventTarget` support)
## Installation
```bash
npm install @lyleunderwood/filereader-polyfill
```
## Usage
### Basic Usage
```javascript
import { FileReader, File } from '@lyleunderwood/filereader-polyfill';
// Reading from a Blob
const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
const reader = new FileReader();
reader.onload = (event) => {
console.log('Result:', reader.result);
};
reader.readAsText(blob);
```
### Reading from File Paths (Node.js specific)
```javascript
import { FileReader, File } from '@lyleunderwood/filereader-polyfill';
// Create File from file path
const file = new File('/path/to/your/file.txt');
const reader = new FileReader();
reader.onload = () => {
console.log('File content:', reader.result);
};
reader.readAsText(file);
```
### Global Polyfill
The polyfill automatically installs itself globally if `FileReader` or `File` don't exist:
```javascript
import '@lyleunderwood/filereader-polyfill';
// Now you can use FileReader and File globally
const reader = new FileReader();
const file = new File(['data'], 'example.txt');
```
### Event Handling
```javascript
const reader = new FileReader();
reader.onloadstart = () => console.log('Started reading');
reader.onprogress = (event) => {
if (event.lengthComputable) {
const progress = (event.loaded / event.total) * 100;
console.log(`Progress: ${progress}%`);
}
};
reader.onload = () => console.log('Reading completed');
reader.onerror = () => console.error('Error occurred:', reader.error);
reader.onabort = () => console.log('Reading aborted');
reader.onloadend = () => console.log('Reading finished');
// You can also use addEventListener
reader.addEventListener('load', (event) => {
console.log('Load event:', event);
});
```
### Reading Different Formats
```javascript
const reader = new FileReader();
const blob = new Blob(['Hello, World!']);
// Read as text
reader.readAsText(blob);
// Read as ArrayBuffer
reader.readAsArrayBuffer(blob);
// Read as Data URL
reader.readAsDataURL(blob);
// Read as binary string
reader.readAsBinaryString(blob);
```
### Abort Reading
```javascript
const reader = new FileReader();
const blob = new Blob(['Large file content...']);
reader.readAsText(blob);
// Abort the reading operation
setTimeout(() => {
reader.abort();
}, 100);
```
### Error Handling
The polyfill provides W3C-compliant error handling with proper DOMException types:
```javascript
const reader = new FileReader();
// Handle different error scenarios
reader.onerror = (event) => {
console.log('Error name:', reader.error.name);
console.log('Error message:', reader.error.message);
};
reader.onabort = (event) => {
console.log('Operation was aborted');
// reader.error will be AbortError
};
// Reading from non-existent file (Node.js path constructor)
try {
const file = new File('/path/to/nonexistent/file.txt');
reader.readAsText(file); // Will trigger error event with NotReadableError
} catch (error) {
// File constructor errors are deferred to read operations
}
// Attempting to read while already reading
try {
reader.readAsText(blob);
reader.readAsText(blob); // Throws InvalidStateError
} catch (error) {
console.log(error.name); // "InvalidStateError"
}
```
## API Reference
### FileReader Class
Extends `EventTarget` and implements the complete Web FileReader API.
#### Properties
- `readyState`: Current state (`EMPTY`, `LOADING`, `DONE`)
- `result`: The file's contents (available after successful read)
- `error`: Error information if reading failed
#### Methods
- `readAsText(blob, encoding?)`: Read as text with optional encoding
- `readAsDataURL(blob)`: Read as Data URL
- `readAsArrayBuffer(blob)`: Read as ArrayBuffer
- `readAsBinaryString(blob)`: Read as binary string
- `abort()`: Abort the current reading operation
#### Events
- `loadstart`: Reading started
- `progress`: Reading progress (includes `loaded` and `total` properties)
- `load`: Reading completed successfully
- `error`: Error occurred during reading
- `abort`: Reading was aborted
- `loadend`: Reading finished (success, error, or abort)
### File Class
Extends native `Blob` and supports both Web API and Node.js file path constructors.
#### Constructors
```javascript
// Web API style
new File(fileBits, fileName, options)
// Node.js file path style
new File(filePath)
```
#### Properties
- `name`: File name
- `lastModified`: Last modification timestamp
- `size`: File size in bytes
- `type`: MIME type
- `path`: File path (Node.js specific, when created from path)
## TypeScript Support
Full W3C specification-compliant TypeScript definitions are included:
```typescript
import {
FileReader,
File,
type IFileReader,
type IFile,
type NodeFile,
type EventHandler,
type ProgressEvent,
type FileReaderState,
type BlobPart,
type FilePropertyBag
} from '@lyleunderwood/filereader-polyfill';
// W3C compliant usage
const reader: FileReader = new FileReader();
const file: File = new File(['content'], 'example.txt');
// Type-safe event handlers
reader.onload = (event: Event) => {
console.log(reader.result);
};
reader.onprogress = (event: Event) => {
const progressEvent = event as ProgressEvent;
if (progressEvent.lengthComputable) {
const progress = progressEvent.loaded / progressEvent.total;
console.log(`Progress: ${Math.round(progress * 100)}%`);
}
};
// File constructor with proper types
const fileFromBits: File = new File(
['Hello, World!'] as BlobPart[],
'hello.txt',
{ type: 'text/plain', lastModified: Date.now() } as FilePropertyBag
);
// Node.js specific file path constructor
const fileFromPath: NodeFile = new File('/path/to/file.txt');
console.log(fileFromPath.path); // string | undefined
```
### Available Types
- **`IFileReader`**: W3C specification compliant FileReader interface
- **`IFile`**: W3C specification compliant File interface
- **`NodeFile`**: Extended File interface with Node.js specific `path` property
- **`EventHandler`**: W3C compliant event handler function type
- **`ProgressEvent`**: Progress event interface with `lengthComputable`, `loaded`, and `total`
- **`FileReaderState`**: Enum for FileReader states (EMPTY, LOADING, DONE)
- **`BlobPart`**: Union type for valid Blob constructor parts
- **`FilePropertyBag`**: Options interface for File constructor
## W3C Specification Compliance
This polyfill implements the [W3C File API specification](https://www.w3.org/TR/FileAPI/) with 100% compliance for Node.js environments. It includes comprehensive test coverage that verifies:
- ✅ All FileReader interface requirements (constants, attributes, methods, events)
- ✅ All File interface requirements (constructor, properties, Blob inheritance)
- ✅ Proper error handling with correct DOMException types
- ✅ Event firing order and timing per specification
- ✅ State management and concurrent operation prevention
- ✅ Progress events with proper `lengthComputable`, `loaded`, and `total` properties
### Known Limitations
Due to the Node.js environment, some aspects cannot be implemented with 100% browser fidelity:
- **File system access**: Cannot detect file changes after File creation (deferred to read operations)
- **Event objects**: Uses generic Event instead of native ProgressEvent (properties are preserved)
- **Microtask timing**: Event firing may differ slightly from browser microtask scheduling
- **Memory management**: Cannot replicate exact browser garbage collection behavior
- **Security restrictions**: Cannot enforce same-origin policies or file access permissions
These limitations do not affect functional compatibility with the W3C specification.
## Browser Compatibility
This polyfill is designed for Node.js environments. For browser usage, use the native FileReader API which is widely supported.
## Testing
Run the comprehensive test suite including W3C specification compliance tests:
```bash
npm test
```
The test suite includes:
- Unit tests for all functionality
- W3C File API specification compliance tests
- Error handling and edge case coverage
- Integration tests between File and FileReader
## Contributing
Contributions are welcome! Please ensure all tests pass and maintain W3C specification compliance when making changes.
## License
MIT License - see LICENSE file for details.