Reading legacy .xls files
Open binary Excel 97-2003 workbooks and convert them to the modern format.
Install#
dart pub add excel_plusimport 'package:excel_plus/excel_plus.dart';The same call as .xlsx#
Excel.decodeBytes looks at the bytes and picks the right parser, so a legacy workbook opens through exactly the same call, with no extra dependency and no separate API.
final excel = Excel.decodeBytes(File('legacy.xls').readAsBytesSync());
print(excel.tables.keys);What is carried over#
- Cell values of every type
- Dates, in both the 1900 and 1904 epochs
- Merged cells, sheet order and sheet visibility
- Number formats, fonts, fills, borders and alignment
- Column widths and row heights
- Formulas, decoded from the binary token stream back into formula text, including shared and array formulas, keeping the last calculated result as the cached value
Where the decoder meets a token stream it does not model, it falls back to the cached result rather than failing the file.
Converting to .xlsx#
Reading .xls is deliberately read only. Saving always produces a modern .xlsx, which makes this the migration path for a pile of old spreadsheets.
final excel = Excel.decodeBytes(File('legacy.xls').readAsBytesSync());
File('modern.xlsx').writeAsBytesSync(excel.save()!);Converting a folder of them#
import 'dart:io';
import 'package:excel_plus/excel_plus.dart';
void main() {
for (final file in Directory('old').listSync().whereType<File>()) {
if (!file.path.toLowerCase().endsWith('.xls')) continue;
final excel = Excel.decodeBytes(file.readAsBytesSync());
final target = file.path.replaceAll(RegExp(r'\.xls$', caseSensitive: false), '.xlsx');
File(target).writeAsBytesSync(excel.save()!);
}
}Files that will not open#
Password protected workbooks and pre-BIFF8 files (Excel 5.0 and earlier) throw a clear error rather than returning something half parsed. If a file fails, checking whether it opens in a spreadsheet program is usually the fastest way to tell a genuinely corrupt file from an unsupported one.