electron: 9.0.0 does not display local images

Preflight Checklist

  • I have read the Contributing Guidelines for this project.
  • I agree to follow the Code of Conduct that this project adheres to.
  • I have searched the issue tracker for an issue that matches the one I want to file, without success.

Issue Details

  • Electron Version:
    • v9.0.0
  • Operating System:
    • Windows 10
  • Last Known Working Electron version:
    • v8.3.0

Expected Behavior

My app shows a gallery of images from local disk. Electron 8.2.0 would show images. v9.0.0 shows blanks with error:

GET file:///C:/VHA/vha-videos/filmstrips/6f3acc0aeee45a7af5404e3849d8719f.jpg 
net::ERR_UNKNOWN_URL_SCHEME

Actual Behavior

App does not show local images

To Reproduce

Clone https://github.com/whyboris/Video-Hub-App Run the app, create a “hub” (give it a folder with videos) and it will create a gallery of images. Exit the app, update to Electron 9 (comment out 2 lines of shell.openItem) and run app again

Screenshots

image

Additional Information

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 25
  • Comments: 39 (9 by maintainers)

Commits related to this issue

Most upvoted comments

A recap for anyone else who’s having this issue:

  1. Make sure you’ve enabled webSecurity from your BrowserWindow settings as pre-update:
win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      nodeIntegrationInWorker: true,
      webSecurity: false,
    },
  });
  1. Register a file protocol for handling local files. The electron docs recommends you wrap this registration so that it only registers when the app is ready.
import { protocol } from "electron";

app.whenReady().then(() => {
  protocol.registerFileProtocol('file', (request, callback) => {
    const pathname = request.url.replace('file:///', '');
    callback(pathname);
  });
});
  1. If you’re also finding that URI’s with spaces are not working in your application, wrap the pathname with decodeURI(). This will undo any conversion of characters the browser may have added to the file.
import { protocol } from "electron";

app.whenReady().then(() => {
  protocol.registerFileProtocol('file', (request, callback) => {
    const pathname = decodeURI(request.url.replace('file:///', ''));
    callback(pathname);
  });
});

As of my understanding the issue is that when websecurity is disabled AND the file is loaded via http: the file: url scheme is not enabled as a url scheme. My hacky solution is to pretend the file: url scheme is a custom file scheme. And just pass the file path through. This works great in development mode ( i.e. when you have webpack-dev-server serve the app via http: ), but gets overriden by the ‘real’ file: url scheme when packaged ( since in this case the document gets loaded via the file: url scheme in the first place ). You can do it like so:

protocol.registerFileProtocol('file', (request, cb) => {
    const pathname = request.url.replace('file:///','')
    cb(pathname)
});

I dont know if this is really a good idea though, in theory this is great since it allows developers to specify custom request guards for the filesystem, but then again how useful is it when it only works in development mode?

EDIT: After playing around a bit more, my solution for now is to declare a custom file scheme and use that for all resources in disk: In my case it’s media:// but a similar thing could apply to you as well @whyboris

  1. This has the advantage of being more explicit, so a developer can tell at a glance that it’s a custom protocol and therefore is not confused when it doesnt behave like file:
  2. It’s consistent. Overriding the file: scheme is a bad idea since it behaves differently in production
  3. It’s potentially more secure. With file: you allow full access to you filesystem, but with a custom fileProtocol you ( the main process ) get’s to decide wether or not a renderer process should have access. Also you can have webSecurity enabled in dev mode with this. The only real issue I see is that this approach runs the risk of reinventing the wheel. The file scheme is not blocked by default without a reason.

Just FYI, my original Issue (of not showing images) is broken on both Windows & Mac (I tested on both) 😓

@codebytere This problem occurs when the audio tag uses an absolute path to load local resources when the page is loaded using the http protocol, and it works fine when using a relative path.

Platform: Windows 10 / Linux(Ubuntu 18.10)

repro: https://github.com/lyswhut/test-load-local-file

  1. First run npm run server
  2. Then npm run start2

Windows 10 1

Linux(Ubuntu 18.10) 1

I can also verify it isn’t Windows-specific. I am experiencing the problem on macOS 10.14.6.

Thanks! Just checked on macOS and it ran correctly so it looks like a Windows specific problem. I’ll dig in more as soon as i can.

any update ? same problem electron v11,

@Bug-Reaper This workaround solves the problem where you use a http server to serve your app in development and still want to use the file url scheme to load images / json an the like. However, everything you execute in the registerFileProtocol callback in your example will not be executed when the app us served via the file protocol itself. (i.e. in a production environment) This is fine if you just want to polyfill the weird electron behaviour, but as soon as you’re also putting resolving guards or custom logic in the file handler you have to use a custom protocol like media:/// To do this you just have to replace file with media:

import { protocol } from "electron";

app.whenReady().then(() => {
  protocol.registerFileProtocol('media', (request, callback) => {
    const pathname = decodeURI(request.url.replace('media:///', ''));
// some custom resolver logic here
    callback(pathname);
  });
}); 

@ethan-ou’s solution works except with some special characters. For example it fails with a file with this name:

!@#$%^&'";<>.jpg

However if you replace decodeUri with decodeURIComponent it works even with fishy file names, e.g.:

import { protocol } from "electron";

app.whenReady().then(() => {
  protocol.registerFileProtocol('file', (request, callback) => {
    const pathname = decodeURIComponent(request.url.replace('file:///', ''));
    callback(pathname);
  });
});

Is this no longer possible? If I follow this exactly nothing happens. But, if I use a custom protocol instead, it works. Are we no longer able to registerFileProtocol for file?

The bug seems to persist somehow still in electron 13.1.9. I am able to just pass a local image path to an <img src="C:/test.jpg" /> tag and the image will show up fine. However, when I take the same URL and set it as background of a <div> like this: <div style="background-image: url('file:///C:/test.jpg')" /> it won’t load and the console says: ERR_FILE_NOT_FOUND.

Registering the file protocol manually as mentioned above does not change anything.

@JonasKruckenberg Re: your edit:

Will something like :

import { protocol } from "electron";

app.whenReady().then(() => {
  protocol.registerFileProtocol('file', (request, callback) => {
    const pathname = decodeURI(request.url.replace('file:///', ''));
    callback(pathname);
  });
});

Not work in production. If so how would I alter that sample to use “media:///” as you describe?

yes, when image path contains querystring it breaks completely to stop image shown. and report below error message,

GET file:///C://clock.jpg?1593534885821 net::ERR_FILE_NOT_FOUND

those src path could work normal before electron 9.

The same problem. It can’t work well when I upgrade the 9.0.0 version on Mac.

As for Electron 25.1.1 (tested on mac OS only) and to keep your webSecurity enabled, add custom protocol when creating your browser window

const createWindow = (): void => {
  mainWindow = new BrowserWindow({
    height: 600,
    width: 800,
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
  })
  mainWindow.webContents.session.protocol.registerFileProtocol(
    'media',
    (request, callback) => {
      const pathname = decodeURIComponent(request.url.replace('media:///', ''))
      callback(pathname)
    }
  )
  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY)
}

Then load your image with media:// protocol

<Image src={'media:///Users/.../image.jpg'} />

It is useful to add the following code to the created window.

My requirement is to upload a local file.

win.webContents.session.protocol.registerFileProtocol('file', (request, callback) => {
    const url = request.url.substr(8)
    callback(decodeURI(url))
})

yes, when image path contains querystring it breaks completely to stop image shown. and report below error message, GET file:///C://clock.jpg?1593534885821 net::ERR_FILE_NOT_FOUND those src path could work normal before electron 9.

same problem, in electron 8.x, c://localpath/hello.jpg?param=1 work fine. But in 9,get an error. is there any way to fix it in electron 9?

What you, @venuseo are reporting is caused by the fact that query strings are not a thing in file urls as per RFC8089 They exist in the Http and https Url standard but not in the file Url standard. ( How would they translate to file system paths anyway? ) So using query strings in file Urls is pretty hacky and is only possible because the chromium Url parser apparently ignores query strings in file Urls. Our solution doesn’t however since all it does is URI decoding and removing the file:/// part. So your Url file:///c://localpath/hello.jpg?param=1 will be turned into c://localpath/hello.jpg?param=1 which obviously is not a working filesystem path. What you could do to replicate the electron 8.x behaviour, however, is to split the parsed path at the first question mark and return the first element in the array:

import { protocol } from "electron";

app.whenReady().then(() => {
  protocol.registerFileProtocol('file', (request, callback) => {
    const pathname = decodeURI(request.url.replace('file:///', ''));
    const parts = pathname.split('?')
    callback(parts[0]);
  });
});

I would discourage you from using query strings completely though since standards exist for a reason Maybe find a different solution like rerendering the img tag for example.

still does not display local image if url contains question mark, such as ‘file:///c:/images/clock.jpg?time=16’

This is not because of this bug, but because the querystring is part of the http url scheme not of the file url scheme. See This StackOverflow answer This is pretty much intended behavior as there is no server to parse the querystring with file urls anyway.

Edit: Or do you mean it breaks completely?

I successfully replicated the problem with https://github.com/lyswhut/test-load-local-file. I just had to replace the line const filePath = dir + '/t-rex-roar.mp3' with const filePath = 'file://' + dir + "/t-rex-roar.mp3";.

I have updated repro and Linux has the same problem: https://github.com/electron/electron/issues/23757#issuecomment-634829046

Switch to electron@8.3.0 to resume work

same problem. In v9.0.0, when browserWindow uses http protocol to load the page, the audio tag cannot load the local audio file, When the file protocol is used to load the page, the local audio file can be loaded normally.

The last working version: v8.3.0

@whyboris could you please provide a more minimal sample? That app is rather large which makes it harder for us to isolate the Electron-mediated issue separate from the rest of the app’s workings.