Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opening images in development errors #99

Closed
holywyvern opened this issue Feb 17, 2018 · 21 comments
Closed

Opening images in development errors #99

holywyvern opened this issue Feb 17, 2018 · 21 comments

Comments

@holywyvern
Copy link

When I try to read an Image from a file inside my documents folder (e.g. C:/users/me/Documents/test.png) doesn't work, the loader keeps intercepting the file and trying to find it locally on the project directory.

It prints this error:

Not allowed to load local resource: file:///users/me/Documents/test.png

This problem only applies on development, when the app is on production it works as expected and the image is displayed without trouble.

@Circuit8
Copy link

Circuit8 commented Feb 21, 2018

I'm having a similar error. When I reference a file which a user has uploaded from a file input field, and try to display the image I get this error:

GET http://localhost:9080/home/joshua/Programs/my-project/static/1.jpeg 404 (Not Found)

@loopmode
Copy link
Collaborator

I think these are different problems. Or maybe two sides of the same problem,

@holywyvern What OS are you on? A colleague of mine on Mac had to start the dev server with sudo, because of some troubles. Might be similar..?

@Circuit8 Do you get this error in development or in production? Because.. In your case you try to load an image via http protocol from localhost.
(not file protocol from user documents folder, as in the other case)

localhost:9080 is the default development server - and it is not available to your bundled app. Only when running yarn dev.

@holywyvern
Copy link
Author

@loopmode Windows 10 standard/pro (I have two PCS on both it has the same problem), 64 bits.

I think it may be than the webpack dev server or something may be trying to load it as a relative resource and fails.

I tried to do that anyway and ran my powershell as administrator, but the problem is still there.

Be aware I'm talking than I'm trying to open an image from a folder outside the /static or the project.
The project may be at C:\users\me\project and my image is in C:\users\me\documents\images\test.png

Of course, for now it works when I'm building -> runing the project, but I miss all the development features while doing that.

@loopmode
Copy link
Collaborator

loopmode commented Feb 24, 2018

Well I didn't have much luck with the static folder myself, but loading from documents folder worked nicely.
Note that you do have to explicitly set the file: protocol.
Also, I always resolved the paths as in:

const pathToImage = path.resolve(app.getPath('pictures'), 'some/path/to/image.png))
...
<img src={`file:${pathToImage}`} />
// -> "file:c:\users\<currentuser>\pictures\some\path\to\image.png"
// -> "file:~/pictures/some/path/to/image.png"

Actually, after checking actual project, I did not always resolve, so I ended up with stuff like:
<img class="TitleImage" src="file:C:\Users\loopm\Documents/jbm-app/images/title.png">

still it works nicely, So, while mixed forward/backward slashes apparently don't matter much, the file: was definitely crucial.

@Circuit8
Copy link

Circuit8 commented Feb 24, 2018

@loopmode I get it in development. I kind of get what your saying. How then do I select an image on my dev server?

@loopmode
Copy link
Collaborator

loopmode commented Feb 25, 2018

@Circuit8 Well, you don't.. :)
Initially, when I came to electron, I thought "ah, well I have an integrated node server, so I can do the classic client/server REST architecture", but that's not the case!
You either go the non-trivial route of setting up an express server inside electron - there are quite some articles and tutorials online - or you simply drop the idea, like I did. And instead, you embrace the fact that you have a full file system available, and simply store everything on disk.

What you can do is to put uploads on the filesystem.
You choose a location, e.g. rather anonymously somewhere inside the working dir of the app, e.g. on Windows C:\Users\<username>\AppData\Roaming\<appname>\the-uploads-folder, or you choose to do it somewhere more publicly in the user's folder, e.g. on Windows C:\Users\<username>\Documents\the-uploads-folder.

Regarding uploads, the simplest thing you can do is - since the user probably picks the image on the very same machine your app is running on? - to just copy a file picked via showOpenDialog to your custom directory.
You could also naively read the contents of the picked file and save them elsewhere, e.g. as a base64 encoded string, but that will most likely mess up filesizes and ignore compression as in e.g. jpg. So, probably copying is better.

Some links around this topic:
https://github.com/electron/electron/blob/master/docs/api/dialog.md
https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname
https://stackoverflow.com/questions/38067298/saving-files-locally-with-electron
https://dzone.com/articles/upload-files-or-images-to-server-using-nodejs

@holywyvern sorry for hijacking your issue here. Strayed off topic again :)

@Circuit8
Copy link

@loopmode thanks so much for that explanation it's all beginning to make sense now. Cheers!

@loopmode
Copy link
Collaborator

You're welcome! Go make a cool app! :)

@holywyvern
Copy link
Author

holywyvern commented Feb 27, 2018

I tried to put only file:, but the problem is still there...

Here it's what my code is actually doing:

components/ImagePreview.js

import React from "react";
import { format as formatUrl } from "url";

const ImagePreview = ({ src }) => {
    return (
        <div className="img-preview">
            <div className="img-container"> 
                <img src={formatUrl({
                    pathname: src,
                    protocol: 'file',
                    slashes: true
                })} />
            </div>
        </div>
    );
};

export default ImagePreview;

src is the absolute path to file.
The file in src of course exists and it works fine on production, the problem exists in development.

I tried changing the formatUrl as:

<img src={`file:${src}`} />

But it doesn't work on development, yet again, it does on production.

I would brush it off as a computer's problem on my environment, but I have my PC at home wich does the exact same thing.

It should be noted, than even using the console fails and it's not a React problem either:

let img = new Image();
img.src = require("url").format({ 
       pathname: "C:\Users\RAMIRO\Documents\test.png", 
       protocol: 'file', 
       slashes: true 
});

@hugohil
Copy link

hugohil commented Mar 15, 2018

For those who have the possibility to do so, you can disable some security restriction from Chrome in Electron:

  const window = new BrowserWindow({
    webPreferences: {
      webSecurity: false
    }
  }

Note that you should not use this if your app is to be downloaded and install publicly.

@mrsimonemms
Copy link

I "solved" it by grabbing the image contents as a buffer, converting to base64 and then displaying the base64 string in the HTML

const logoPath = '/path/to/my/image.png';

const logo = fs.readFileSync(logoPath).toString('base64');
<img :src="'data:image/jpg;base64,' + logo" />

It doesn't seem to matter what the mime type is - I've used image/jpg despite my logo being PNG and it works quite cheerfully. Not sure how wise it is to use fs.readFileSync either, but it works fine in my implementation

@payne8
Copy link

payne8 commented Dec 6, 2018

Adding some info to help narrow down the issue.
I see this issue in development. Here is the jsx I am using (but I imagine this would come up even without jsx):

<img src={`file:${icon.location}`}/>

When the image tag is shown in the DOM in the dev console it looks like this:

<img src="file:C:\Users\Mason\AppData\Roaming\Electron\userImages\tYzBXzcK.bmp">

I get the following error in the console:

Not allowed to load local resource: file:///C:/Users/Mason/AppData/Roaming/Electron/userImages/tYzBXzcK.bmp

But, and here is the kicker, when I hover over the src value in the dev console, or copy the link address from the context menu I get this http://localhost:9080/file:C:\Users\Mason\AppData\Roaming\Electron\userImages\tYzBXzcK.bmp

Note: even though I have prepended the src with file: something is still making the links start with http://localhost:9080/

@Dominic-Preap
Copy link

I resolved my problem by using this:

import * as url from 'url';
import { isDevelopment } from './utils';

const src = (val: string) => (isDevelopment ? url.resolve(window.location.origin, val) : path.join(__static, val));
export const Splashscreen = () => <img className="img-middle" height="80px" src={src('./images/logo.png')} />;

@felipebutcher
Copy link

felipebutcher commented Mar 19, 2019

I "solved" it by grabbing the image contents as a buffer, converting to base64 and then displaying the base64 string in the HTML

const logoPath = '/path/to/my/image.png';

const logo = fs.readFileSync(logoPath).toString('base64');
<img :src="'data:image/jpg;base64,' + logo" />

It doesn't seem to matter what the mime type is - I've used image/jpg despite my logo being PNG and it works quite cheerfully. Not sure how wise it is to use fs.readFileSync either, but it works fine in my implementation

after a bloody battle this worked for me. but also gave me a memory leak since I call this 4 times every second. also I realized later this problem affected also other parts of my app (the app plays videos and they were not working as well).

what really solved my issue was this: electron/forge#220 (comment)

oh... and that's crazy: the issue for me only happened in production and not in dev... lol

my app uses electron-forge so I am not sure the same solution will help you guys, but sending in a hope.

thanks!

@loopmode
Copy link
Collaborator

I resolved my problem by using this:

import * as url from 'url';
import { isDevelopment } from './utils';

const src = (val: string) => (isDevelopment ? url.resolve(window.location.origin, val) : path.join(__static, val));
export const Splashscreen = () => <img className="img-middle" height="80px" src={src('./images/logo.png')} />;

Of course this only applies to loading images from __static, but still: This is an excellent strategy, and I totally recommend it.
Here's how I use it: https://github.com/loopmode/electron-webpack-quick-start/blob/49a48f69465df9303dc48b5a215bcde01fa41f56/src/renderer/App.js

This is the result, by the way:
image

@loopmode
Copy link
Collaborator

Closing this issue for now, as there are a couple of ways to solve this problem, including pretty robust ones.

As mentioned in my last comment, here is one way to solve the problem:

  • Use the /static folder together with the __static global, put your images in there
  • define a helper function that distinguishes development and production modes
  • in your image src, use the helper function

@obermillerk
Copy link

obermillerk commented Jul 21, 2020

While the helper is a working strategy, I'm not sure I'd call it "excellent". I think the discrepancy between development and production static asset strategies should be fixed rather than worked around, though I see how it would be a very difficult thing to solve because of the differences in how the two environments retrieve content. At the very least, I would say this issue should be referenced in the docs for static assets, if not discussed in some detail in the docs themselves since any notion of this problem is currently absent.

@loopmode
Copy link
Collaborator

Or we simply put such a helper (probably somewhat improved) into the library itself, and document it as the way to embed static assets. What do you think?

@obermillerk
Copy link

I think that'd be good, for a start at least. I'd still ideally like to see some way to just specify a single path-like in the img tag (which would be much more familiar to anyone with web development background), but that's probably not simple if even possible.

@loopmode
Copy link
Collaborator

Not quite sure I understand what you mean. Are you suggesting that e.g. <img src="./image.png" /> should be loading the file image.png from the static folder automatically?

@obermillerk
Copy link

In an ideal world, yes. Though I'm not sure how viable that would be. I imagine there might be some issues with that since the retrieval is being done by the chromium engine in electron. Perhaps electron-webpacker could process src attributes on html elements though and do something similar to this proposed integrated helper function? I'm not too familiar with the inner workings of it so not sure whether that's possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants