Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Electron - throws Not allowed to load local resource when using showOpenDialog

Tags:

electron

I just wanted to use showOpenDialog and load an image. But when I select an image app will crash.

main.js:

...
  ipcMain.on('open-file-dialog', function (event) {
    const window = BrowserWindow.getFocusedWindow();

    dialog.showOpenDialog(window, {
      properties: ['openFile']
    }, p => {
      console.log(p)
    });
  })

renderer.js:

document.querySelector('#select-image').addEventListener('click', function (event) {
    ipcRenderer.send('open-file-dialog')
});

When I select anything this error shows in console : Not allowed to load local resource: file:///start and the Electron version is 8.2.5

Edit 1:

This warning (or may be error) is shown in the terminal objc[50899]: Class FIFinderSyncExtensionHost is implemented in both /System/Library/PrivateFrameworks/FinderKit.framework/Versions/A/FinderKit (0x7fff951e61d0) and /System/Library/PrivateFrameworks/FileProvider.framework/OverrideBundles/FinderSyncCollaborationFileProviderOverride.bundle/Contents/MacOS/FinderSyncCollaborationFileProviderOverride (0x11298bdc8). One of the two will be used. Which one is undefined.

Edit 2: I put together sample Gist using Electron Fiddle : here

like image 943
Daniel Avatar asked May 05 '20 21:05

Daniel


1 Answers

Electron doesn't allow windows with webSecurity: true to load local files.

Bad solution

One way to get rid of the error is to simply set it to false. But this will make your app less safe to use:

new BrowserWindow({
  ...
  webPreferences: {
    webSecurity: false
  }
})

Good solution

Instead, what you have to do is to create a custom protocol and then use that for loading files.

Step 1: Create a custom protocol

Main process:

import { protocol } from 'electron'

...

app.on('ready', async () => {
  // Name the protocol whatever you want.
  const protocolName = 'your-app-name'

  protocol.registerFileProtocol(protocolName, (request, callback) => {
    const url = request.url.replace(`${protocolName}://`, '')
    try {
      return callback(decodeURIComponent(url))
    }
    catch (error) {
      // Handle the error as needed
      console.error(error)
    }
  })
  ...

Note: I'm not sure if the protocol name has to be unique, I never tested it. If you just name the protocol as safe-protocol and the user of your app has another app X with registered safe-protocol, your app will either throw an error when they open it or it will register both apps but when the user tries to open their app from a URL, using app.setAsDefaultProtocolClient function, both of those apps will open. Not sure what happens in this case.

Step 2: use protocol to load files

Method 1: get path from the main process:

Main process:

ipcMain.on('open-file-dialog', function (event) {
  const window = BrowserWindow.getFocusedWindow();

  dialog.showOpenDialog(window, { properties: ['openFile'] })
    .then(result => {
      // Send the path back to the renderer
      event.sender.send('open-file-dialog-reply', { path: result.filePaths[0] })
    })
    .catch(error => {
       console.log('Could not get file path')
    })
})

Renderer process:

<img id="image-1">
ipcRenderer.on('open-file-dialog-reply', (event, data) => {
  const path = data.path
  loadImage(path)
}

function loadImage (path) {
  const image1 = document.getElementById('image-1')
  image1.src = `safe-file-protocol://${path}`
}

Method 2: load path directly in the renderer:

Renderer process:

<img id="image-1" data-path="C:/test.jpg">
function loadImage () {
  const image1 = document.getElementById('image-1')
  const path = image1.dataset.path
  image1.src = `safe-file-protocol://${path}`
}

loadImage()
like image 148
AlekseyHoffman Avatar answered Jan 01 '23 09:01

AlekseyHoffman