-1

Problem I'm trying to send multiple files from the renderer process to the main process using Electron's IPC, but the file objects arrive empty. Renderer Process:

if (files?.length > 1) {
  window.electron.ipcRenderer.send('multipleFiles', files)
}

Main Process:

ipcMain.on('multipleFiles', (event, payload) => {
  console.log("Received multipleFiles");
  console.log(payload);
  
  Object.keys(payload).map((key) => {
    console.log(payload[key].data) // undefined or empty
  })
})

What's Happening

The iteration runs the correct number of times (matching files.length) However, each element in the payload is just an empty object {} The file data is not being transferred

What I've Tried I can confirm that files contains valid File objects in the renderer process before sending, but they lose their data when received in the main process. Question Why are the File objects arriving empty in the main process, and how can I properly transfer file data through Electron's IPC? Environment:

Electron version: 38.1.2

Reproduction: Run npm create @quick-start/electron, and then set renderer.js to

<script lang="ts">

  const ipcHandle = (): void => window.electron.ipcRenderer.send('ping')

  function handleFileSelect(event: Event): void {
    const input = event.target as HTMLInputElement
    const files = input.files

    if (files?.length === 1) {
      console.log(`Processing ${files.length} file(s)`)

      for (const file of Array.from(files)) {
        const reader = new FileReader()

        reader.onload = () => {
          if (reader.result instanceof ArrayBuffer) {
            console.log('About to send:', file.name)
            window.electron.ipcRenderer.send('file-upload', {
              name: file.name,
              type: file.type,
              data: reader.result
            })
          } else {
            console.error('Expected ArrayBuffer but got:', typeof reader.result)
          }
        }

        reader.onerror = () => {
          console.error('FileReader error:', reader.error)
        }

        reader.readAsArrayBuffer(file)
      }
    } else if (files?.length > 1) {
      window.electron.ipcRenderer.send('multipleFiles', files)
    }
  }
</script>

<div class="creator">Powered by electron-vite</div>
<div class="text">
  Build an Electron app with
  <span class="svelte">Svelte</span>
  and
  <span class="ts">TypeScript</span>
</div>
<p class="tip">Please try pressing <code>F12</code> to open the devTool</p>
<div class="actions">
  <div class="action">
    <a href="https://electron-vite.org/" target="_blank" rel="noreferrer">Documentation</a>
  </div>
  <div class="action">
    <a target="_blank" rel="noreferrer" onclick={ipcHandle}>Send IPC</a>
    <input type="file" multiple id="sheetSelection" onchange={handleFileSelect} />
  </div>
</div>

and add the following to main.js


ipcMain.on('file-upload', (event, payload) => {
  console.log("Received file:", payload.name, payload.type)
  console.log("Data size:", payload.data.byteLength, "bytes")

  // Now you can work with the data
  // ... rest of your XLSX processing
})


ipcMain.on('multipleFiles', (events, payload)=>{
  console.log("Received multipleFiles");
  console.log(payload)
  Object.keys(payload).map((key)=>{
    console.log(payload[key].data)
  })
})

1
  • Why would you expect that the file content could be transferred, ever?! And what is window.electron? The question looks very weird. The entire idea is wrong. The scenarios of using IPC with files are very different. If you explain your ultimate goal, I will be able to explain what to do. A quick hint: everything works the other way around. Commented yesterday

1 Answer 1

3

As per Electron's documentation, only certain objects "survive" being transferred via IPC. Your use-case seems to let the user select a file using an HTML <input> and then do something with that on the Main process (which is the correct place to do the processing).

As Electron exposes a number of APIs for native OS integration, you don't need to use <input type="file"> and you don't need to read the file on the renderer. Instead, open a file chooser (which also allows opening multiple files), retrieve the selected paths, read the files, and process them – all on the main process. This approach will also enable you to combine the code paths for processing one file and for processing multiple files into a single function.

A little boilerplate to get you started could look like this:

<!-- renderer.html -->
<script>
    function handleFileSelect (e) {
        window.electron.ipcRenderer.send ("select-files");
    }
    
    window.electron.ipcRenderer.on ("files-processed", (event, payload) => {
        // Do something with the result in payload
    });
</script>
<!-- ... -->
<button onclick="handleFileSelect">Select File(s)</button>

and then listen for that event in your main process:

// main.js
const { ipcMain, dialog } = require ("electron");

ipcMain.on ("select-files", (event, payload) => {
    var files = dialog.showOpenDialog (
        your_main_window, // pass your main BrowserWindow object here
        {
            title: "Select a File",
            properties: ["openFile", "multiSelections"]
        }
    );
    
    // Check whether the dialog was cancelled
    // (You may want to send a "processing failed" message to the renderer)
    if (files === undefined) return;
    
    // Otherwise, files is of type string[] containing all selected paths
    // ... Process files ...
    // When you're done, send the result back to the renderer (if any)
    var result = null;
    event.sender.send ("files-processed", result);
});

This of course still requires some work to fill in the missing bits and to integrate it into your app correctly on your part.

On a related note, you may want to take a look into preload scripts which enable you to narrow down the number of functions and objects (of your API and of Electron's API) you're exposing to the renderer process.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.