(
fileStream: ReadableStream<Uint8Array>,
options: {
chunkSize?: number;
batchSize?: number; // Number of rows to process before yielding
delimiter?: string;
onChunkParsed?: (chunk: string[][], headers: string[], isFirstChunk: boolean) => Promise<void>;
onProgress?: (progress: number, stats: StreamingCSVParseStats) => void;
onComplete?: (result: CSVParseResult) => void;
estimatedTotalBytes?: number;
sampleRowsLimit?: number;
} = {}
)
| 78 | * Parse large CSV files in chunks with progress tracking and browser yielding |
| 79 | */ |
| 80 | const parseCSVStream = async ( |
| 81 | fileStream: ReadableStream<Uint8Array>, |
| 82 | options: { |
| 83 | chunkSize?: number; |
| 84 | batchSize?: number; // Number of rows to process before yielding |
| 85 | delimiter?: string; |
| 86 | onChunkParsed?: (chunk: string[][], headers: string[], isFirstChunk: boolean) => Promise<void>; |
| 87 | onProgress?: (progress: number, stats: StreamingCSVParseStats) => void; |
| 88 | onComplete?: (result: CSVParseResult) => void; |
| 89 | estimatedTotalBytes?: number; |
| 90 | sampleRowsLimit?: number; |
| 91 | } = {} |
| 92 | ): Promise<CSVParseResult> => { |
| 93 | const { |
| 94 | chunkSize = 50000, |
| 95 | batchSize = 50000, // Process 5000 rows at a time |
| 96 | delimiter = ',', |
| 97 | onChunkParsed, |
| 98 | onProgress, |
| 99 | onComplete, |
| 100 | estimatedTotalBytes, |
| 101 | sampleRowsLimit = 1000000 |
| 102 | } = options; |
| 103 | |
| 104 | try { |
| 105 | setIsLoading(true); |
| 106 | setError(null); |
| 107 | setProgress(0); |
| 108 | |
| 109 | // Initialize parser stats |
| 110 | const stats: StreamingCSVParseStats = { |
| 111 | bytesProcessed: 0, |
| 112 | rowsProcessed: 0, |
| 113 | chunksProcessed: 0, |
| 114 | totalBytes: estimatedTotalBytes, |
| 115 | startTime: Date.now() |
| 116 | }; |
| 117 | |
| 118 | // Use StreamReader to efficiently process the file in chunks |
| 119 | const streamReader = new StreamReader(fileStream); |
| 120 | |
| 121 | // Variables to accumulate data |
| 122 | let headers: string[] = []; |
| 123 | let headersParsed = false; |
| 124 | let sampleData: string[][] = []; |
| 125 | let buffer = ''; |
| 126 | let lineCount = 0; |
| 127 | const textDecoder = new TextDecoder(); |
| 128 | |
| 129 | // Batch processing variables |
| 130 | let currentBatch: string[][] = []; |
| 131 | let lastYieldTime = Date.now(); |
| 132 | |
| 133 | // Process the stream in chunks |
| 134 | await streamReader.readByChunks(async (chunk) => { |
| 135 | // Decode the chunk and add to buffer |
| 136 | const text = textDecoder.decode(chunk, { stream: true }); |
| 137 | buffer += text; |
no test coverage detected