# How can read local excel from designer?

**URL:** <https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651>\
**Category:** Ignition\
**Created:** [January 30, 2026, 5:13am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651 "2026-01-30T05:13:38Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![philin15](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@philin15](https://forum.inductiveautomation.com/u/philin15)\
**Post date:** [January 30, 2026, 5:13am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/1 "2026-01-30T05:13:38Z")

</div>

```python
import sys
import os
from java.io import FileInputStream
from org.apache.poi.ss.usermodel import WorkbookFactory, CellType
from org.apache.poi.xssf.usermodel import XSSFWorkbook
from org.apache.poi.hssf.usermodel import HSSFWorkbook

def log_error(message, exception=None):

    logger = system.util.getLogger("ExcelReader")
    logger.error(message)
    
    if exception:

        if hasattr(system, 'util') and hasattr(system.util, 'getStackTrace'):
            logger.error("stack error: %s" % system.util.getStackTrace(exception))
        else:

            logger.error("error info: %s" % str(exception))

def get_cell_value(cell):

    if not cell:
        return ""
    
    try:
        cell_type = cell.getCellType()
        
        if cell_type == CellType.STRING:
            return cell.getStringCellValue() or ""
        elif cell_type == CellType.NUMERIC:

            if cell.getCellStyle().getDataFormatString() in [
                'm/d/yy', 'm/d/yyyy', 'mm/dd/yyyy', 'yyyy-mm-dd'
            ]:
                return str(cell.getDateCellValue())
            else:

                num_val = cell.getNumericCellValue()
                if abs(num_val - round(num_val)) < 1e-6:
                    return str(int(num_val))
                else:
                    return str(num_val)
        elif cell_type == CellType.BOOLEAN:
            return str(cell.getBooleanCellValue())
        elif cell_type == CellType.BLANK or cell_type == CellType._NONE:
            return ""
        else:
            return str(cell.toString())
    except:
        return ""

def read_excel_poi(file_path, sheet_name=0, header_row=0):

    try:
  
        if not os.path.exists(file_path):
            log_error("empty : %s" % file_path)
            return []
        

 
        fis = FileInputStream(file_path)
        if file_path.lower().endswith('.xlsx'):
            workbook = XSSFWorkbook(fis)
        else:
            workbook = HSSFWorkbook(fis)
 
        sheet_idx = sheet_name if isinstance(sheet_name, int) else workbook.getSheetIndex(sheet_name)
        if sheet_idx == -1:

            workbook.close()
            fis.close()
            return []
        
        sheet = workbook.getSheetAt(sheet_idx)
        
        data = []
        headers = []
        

        max_rows = sheet.getPhysicalNumberOfRows()
        if header_row < max_rows:
            header_row_obj = sheet.getRow(header_row)
            if header_row_obj:
                num_cols = header_row_obj.getLastCellNum()
                headers = [get_cell_value(header_row_obj.getCell(c)) for c in range(num_cols)]
                headers = [h.strip() for h in headers if h.strip()] 
        

        for row_idx in range(header_row + 1, max_rows):
            row = sheet.getRow(row_idx)
            if row:
                row_data = [get_cell_value(row.getCell(c)) for c in range(row.getLastCellNum())]
                

                if headers:

                    while len(row_data) < len(headers):
                        row_data.append("")
                    row_dict = dict(zip(headers, row_data))
                else:
                    row_dict = {"Col{i+1}": row_data[i] for i in range(len(row_data))}
                
                data.append(row_dict)
        

        workbook.close()
        fis.close()
        

            
        return data
        
    except Exception as e:
        error_msg = "Excel error: %s" % str(e)
        log_error(error_msg, e)
        print(error_msg)
        return []

```

I run this script in button event,but can’t get the data

---

<div class="post-metadata">

**Author:** ![Chris\_Bingham](https://avatars.discourse-cdn.com/v4/letter/c/e5b9ba/32.png) [@Chris\_Bingham](https://forum.inductiveautomation.com/u/Chris_Bingham)\
**Post date:** [January 30, 2026, 5:22am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/2 "2026-01-30T05:22:25Z")

</div>

Are you sure posted the entire script? The last line I see is:  
`sheet = workbook.getSheetAt(sheet_idx)`  
What are you doing with `sheet`?

---

<div class="post-metadata">

**Author:** ![philin15](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@philin15](https://forum.inductiveautomation.com/u/philin15)\
**Post date:** [January 30, 2026, 5:26am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/3 "2026-01-30T05:26:28Z")

</div>

have posted all

---

<div class="post-metadata">

**Author:** ![JVDM](https://avatars.discourse-cdn.com/v4/letter/j/ac8455/32.png) [@JVDM](https://forum.inductiveautomation.com/u/JVDM)\
**Post date:** [January 30, 2026, 6:46am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/4 "2026-01-30T06:46:11Z")

</div>

Could you provide more insight than “can’t get the data”?

What have you tried to solve it?  
Does the code return an error?  
Which parts of the code do work perform as expected? You use a logger for errors. Do they not write anything useful to the logs?

Concider using the logger to e.g.:

- Log the max\_rows value to see whether reading the workbook went successfully
- Log the headers, to see whether you have the right amount of column

---

<div class="post-metadata">

**Author:** ![philin15](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@philin15](https://forum.inductiveautomation.com/u/philin15)\
**Post date:** [January 30, 2026, 7:04am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/5 "2026-01-30T07:04:37Z")

</div>

actually ,it can execute in console

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/3/b33096c7101b2dc3cbea1dacdf10735ae7b00993.png)

but when I run it in button script,the error shows can’t find the file

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/9/59b803b8ec804f31827359f945a8004dfb81e4ec.png)

---

<div class="post-metadata">

**Author:** ![JVDM](https://avatars.discourse-cdn.com/v4/letter/j/ac8455/32.png) [@JVDM](https://forum.inductiveautomation.com/u/JVDM)\
**Post date:** [January 30, 2026, 7:21am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/6 "2026-01-30T07:21:06Z")

</div>

Thanks for sharing this insight.

Try running the code from [this forum page](https://forum.inductiveautomation.com/t/which-scope-is-my-code-running-in/15540/3) in your script console.  
It will tell you whether your script is executed in the Client scope (1), designer scope (2) or gateway scope (0).

If you’re running code in the gateway scope, the console is reading the file on the gateway’s PC, and your designer script is reading the file on your local PC.

---

<div class="post-metadata">

**Author:** ![philin15](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@philin15](https://forum.inductiveautomation.com/u/philin15)\
**Post date:** [January 30, 2026, 8:24am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/7 "2026-01-30T08:24:04Z")

</div>

It’s executed in designer scope,so when it is executed in button events, it’s in gateway scope?

---

<div class="post-metadata">

**Author:** ![JVDM](https://avatars.discourse-cdn.com/v4/letter/j/ac8455/32.png) [@JVDM](https://forum.inductiveautomation.com/u/JVDM)\
**Post date:** [January 30, 2026, 8:31am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/8 "2026-01-30T08:31:28Z")

</div>

I assumed your Script Console scope would be gateway, and your button would be designer/client. My assumption was wrong. Just to test, you could try this code in a button as well, and see if the scopes are the same.

I see you have blurred out your filepath. [This thread](https://forum.inductiveautomation.com/t/filepath-in-script/11112) is \> 10 years old, but might be relevant.

Option 1:

> [@Filepath in script](https://forum.inductiveautomation.com/t/filepath-in-script/11112/2):
>
> Backslashes are special in python (and C, and Java, and …). You need to double them up in string constants.

Option 2:

> [@Filepath in script](https://forum.inductiveautomation.com/t/filepath-in-script/11112/3):
>
> Or change \ to / and let the file system deal with it…

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [January 30, 2026, 4:39pm UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/9 "2026-01-30T16:39:38Z")

</div>

> [@philin15](#):
>
> when I run it in button script

If the button is in **Perspective** , then the script is running on the gateway, not wherever the Perspective session is. This is a fundamental limitation of Perspective's architecture. You **cannot** access local files on the client machine from Perspective, because that would be behavior indistinguishable from malware.

If you want to process a file the user provides in Perspective, the **only option** is the file upload component.

I'll also make a quick note here and say that you should not trust LLMs to help you write Ignition code.

```python
system.util.getStackTrace

```

There is not and has never been a `system.util.getStackTrace` function. Already you are being set up for failure with an LLM, because it's going to happily throw lots and lots of unnecessary code at you that only makes it harder to understand what's going on.

Here's a ready made script you can adapt, graciously provided by @JordanCClark:

> [@Copying an Excel file to a table's dataset](https://forum.inductiveautomation.com/t/copying-an-excel-file-to-a-tables-dataset/34942/13):
>
> Got my paperwork done early, so here's a sample to read from an Excel file EDIT: Reworked a bit to work with both .xls and .xlsx files EDIT: Modified to properly cast data types EDIT: Added handler for formula cell types EDIT: Added parameters to optionally specify sheet, row, and column numbers Reading back a file created from my other example: def excelToDataSet(fileName, hasHeaders = False, sheetNum = 0, firstRow = None, lastRow = None, firstCol = None, lastCol = None): import org.apac…

---

<div class="post-metadata">

**Author:** ![philin15](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@philin15](https://forum.inductiveautomation.com/u/philin15)\
**Post date:** [February 2, 2026, 12:37am UTC](https://forum.inductiveautomation.com/t/how-can-read-local-excel-from-designer/113651/10 "2026-02-02T00:37:03Z")

</div>

Thanks for your information, and I will take a try.
