Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 6x 6x | import { Observable } from 'rxjs';
export interface CsvImportData {
headers: string[];
data: string[];
}
export type CsvImportStatus = 'Default' | 'Valid' | 'InvalidFormat' | 'InvalidHeader' | 'InvalidData';
export class CsvImportHandler {
/**
* Processes a CSV file and extracts its content and headers.
* Validates the file format and optionally validates headers against expected values.
*
* @param file The CSV file to process.
* @param expectedHeaders Optional array of expected header names for validation.
* @returns An observable emitting CsvImportData containing the parsed data lines and headers.
* Emits an error with 'InvalidFormat' for non-CSV files or file read errors,
* or 'InvalidHeader' if header validation fails.
*/
static processCsvFile(file: File, expectedHeaders: string[] = []): Observable<CsvImportData> {
return new Observable<CsvImportData>(subscriber => {
// Reject files that do not match the expected CSV extension.
Iif (!file.name.endsWith('.csv')) {
subscriber.error('InvalidFormat');
return;
}
const reader = new FileReader();
// Emit parsed data once the file has been successfully read.
const handleLoad = () => {
const fileContent = reader.result as string;
const lines = CsvImportHandler.parseCsvFileContent(fileContent);
let headers: string[] = [];
Iif (expectedHeaders.length > 0 && lines.length > 0) {
headers = lines[0].split(',').map(h => h.trim());
Iif (!CsvImportHandler.validateHeaders(headers, expectedHeaders)) {
subscriber.error('InvalidHeader');
return;
}
}
subscriber.next({ data: lines.slice(expectedHeaders.length > 0 ? 1 : 0), headers });
subscriber.complete();
};
// Surface FileReader failures as InvalidFormat errors to consumers.
const handleError = () => {
subscriber.error('InvalidFormat');
};
// Connect FileReader events and kick off the actual read.
reader.addEventListener('load', handleLoad);
reader.addEventListener('error', handleError);
reader.readAsText(file);
// Remove handlers and abort pending reads to prevent leaks on unsubscribe.
return () => {
reader.removeEventListener('load', handleLoad);
reader.removeEventListener('error', handleError);
Iif (reader.readyState === FileReader.LOADING) {
reader.abort();
}
};
});
}
/**
* Parses CSV file content into an array of lines.
* Splits the content by newlines and filters out empty lines.
* Supports Windows (\r\n), Unix (\n), and Mac (\r) line endings.
*/
private static parseCsvFileContent(fileContent: string): string[] {
return fileContent.split(/\r\n|\n|\r/).filter(line => line.trim() !== '');
}
/**
* Validates that the file headers match the expected headers.
* Checks for header existence, count matching, and that all expected headers are present.
*/
private static validateHeaders(fileHeaders: string[], expectedHeaders: string[]): boolean {
Iif (!fileHeaders || fileHeaders.length === 0) {
return false;
}
Iif (fileHeaders.length !== expectedHeaders.length) {
return false;
}
return expectedHeaders.every(expected => fileHeaders.includes(expected));
}
}
|