justkawal / excel

Excel Library for Flutter and Dart - https://pub.dev/packages/excel
MIT License
427 stars 239 forks source link

Loading some excels with multiple sheets yields null check on null issue. #384

Open kuoaid opened 3 weeks ago

kuoaid commented 3 weeks ago

Platform: iOS

Issue: On select files with multiple sheets, the package's code throws a null check on null error.

Steps to reproduce:

  1. Load the file (file_picker) like this, and initialize excel:
  Future<void> pickFile() async {
    bool _fileSuccess = await _fileUploadService.pickFile();
    if (_fileSuccess && _fileUploadService.pickedFile != null) {
      sheetsService = BatchSheetsService(
          car: car, pickedFile: _fileUploadService.pickedFile!);
      sheetsService?.initialize().whenComplete(() {
        matchingServices.initialize(sheetsService!.columns);
        notifyListeners();
      });
    } else {
      devLog("Error: No file was picked or the file pick failed.");
    }
  }
  ///initialize the service
  Future<void> initialize() async {
    await _loadExcelFile(pickedFile);
    fileLoaded = true;
  }

  Future<void> _loadExcelFile(PlatformFile file) async {
    // try {
    devLog("Loading Excel file: ${file.name}");
    var fileBytes = File(file.path!).readAsBytesSync();
    devLog("File bytes: ${fileBytes.length}");
    // If bytes are available, use them
      if (file.bytes != null) {
        devLog("File bytes available.");
      } else if (file.path != null) {
        devLog("File path available: ${file.path}");
        // If bytes are not available, read the file from the path
        File localFile = File(file.path!);
        devLog("Local file: $localFile");
        fileBytes = localFile.readAsBytesSync();
        devLog("File bytes: ${fileBytes.length}");
      } else {
        // If neither bytes nor path are available, throw an error
        throw Exception("No file content available to read.");
      }

      _excel = Excel.decodeBytes(fileBytes);
      devLog("Excel file decoded.");

      if (_excel == null) {
        print("Error: Excel file could not be decoded.");
        return;
      }

      if (_excel!.tables.isEmpty) {
        print("Error: No tables (sheets) found in the Excel file.");
        return;
      }

      selectedSheet = _excel!.tables.keys.first;
      _loadColumns();
      fileLoaded = true;
    // } catch (e) {
    //   print("Error loading Excel file: $e");
    // }
  }
  1. Load the file test_excel_file (test_excel_file.xlsx). It has 2 sheets. You will get:

    [log] Loading Excel file: test_excel_file.xlsx
    [log] File bytes: 5731
    [log] File path available: /private/var/mobile/Containers/Data/Application/BBE734D1-BAB6-43DE-BCA6-D27E8C7F36E4/tmp/test_excel_file.xlsx
    [log] Local file: File: '/private/var/mobile/Containers/Data/Application/BBE734D1-BAB6-43DE-BCA6-D27E8C7F36E4/tmp/test_excel_file.xlsx'
    [log] File bytes: 5731
    [log] Matches initialized: []
    [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Null check operator used on a null value
    #0      Parser._parseTable (package:excel/src/parser/parse.dart:540:9)
    #1      Parser._parseContent.<anonymous closure> (package:excel/src/parser/parse.dart:156:9)
    #2      Iterable.forEach (dart:core/iterable.dart:347:35)
    #3      Parser._parseContent (package:excel/src/parser/parse.dart:154:39)
    #4      Parser._startParsing (package:excel/src/parser/parse.dart:15:5)
    #5      new Excel._ (package:excel/src/excel.dart:63:12)
    #6      _newExcel (package:excel/src/excel.dart:17:20)
    #7      new Excel.decodeBytes (package:excel/src/excel.dart:78:12)
    #8      BatchSheetsService._loadExcelFile (package:***********************batch_upload_sheets_service.dart:52:22)
    #9      BatchSheetsService.initialize (package:***********************batch_upload_sheets_service.dart:28:11)
    #10     BatchAddBackend.pickFile (package:***********************main_backend.dart:90:22)
    <asynchronous suspension>
    <asynchronous suspension>

    This points to issues with these lines' null check:

    var file = _excel._archive.findFile('xl/$target');
    file!.decompress();
  2. However, loading test_excel_file_single (test_excel_file_single.xlsx), which has only one sheet, yields no issue. It still uses the paths to locate the file, as before. If file_picker is specified to include data, the uint8list is used but the overall behavior does not change.

  3. Loading this file - car_service_mods_worksheet 3.xlsx, which has 2 sheets, has no issues. It still uses paths. car_service_mods_worksheet 3.xlsx

Please respond with any insights! Thanks in advance.

kuoaid commented 3 weeks ago

Update - a workaround is to use another package such as spreadsheet_decoder in case of excel package errors. I can decode the excel sheet normally with the other package, extract the data, and reconstruct the Excel object so the rest of my project does not require migration.

Still curious though what causes this bug.