Reading .xlsx in the browser without a spreadsheet library
I run a small site that converts bank CSV exports into the file format QuickBooks Desktop accepts. The whole thing runs client-side, and the privacy claim it makes is unusually literal: every page ships a Content Security Policy with connect-src 'none' , so the browser refuses to let the page make any network request at all. Open the Network tab while you convert a file and it stays empty. That's…
A small website that converts bank CSV exports into a format compatible with QuickBooks Desktop operates entirely in the browser, with strict privacy settings that prevent any network requests. When the website added support for Excel files, the natural choice was the SheetJS library, but the developer decided against it. The reason was not the size of the library, but the fact that using a third-party parser would mean trusting it, which is a weaker claim for a tool handling people's bank statements.
The developer then set out to determine how much of the .xlsx format was actually needed. Upon analysis, they found that less than expected was required. An .xlsx file is a ZIP archive containing XML files: xl/workbook.xml, xl/worksheets/sheet1.xml, xl/sharedStrings.xml, and xl/styles.xml. Reading these files requires three capabilities, two of which are already provided by the browser.
The developer implemented unzipping by walking the ZIP central directory, which took about forty lines of code. Decompression was handled by the native DecompressionStream with the deflate-raw method. The most challenging part was handling dates, as Excel does not store them as dates. A cell containing "5 January 2024" actually holds the number 45296, and whether this number is displayed as a date depends on the cell's number format, which is stored in a different file inside the archive.
The parser had to read the styles.xml file, determine which style indexes correspond to date formats, and check every numeric cell against that list. Dates also use a non-standard epoch (1899-12-30) and older Mac files use a different epoch (1904), requiring additional handling. The developer wrote 26 tests to cover the date handling, sparse cells, shared strings, and sheet selection.
All tests passed, but when the developer tried the parser in a browser, running into a CSP (Content Security Policy) restriction that prevented network requests. Wrapping a stream in a Response and calling arrayBuffer() triggered this CSP violation, even though the stream was never fetched. The developer solved this issue by manually draining the stream. The parser can be found at /src/xlsx/parse.js on qbofile.com.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.