I am currently building a gallery app using Angular 11 + Electron + Java, but I came across this issue that I have not managed to solve.
Using the backend, I get all the files within a folder the user chooses, but then, when i use the paths as src for , Angular adds http://localhost:4200 before the path.
I can't use the asset folder because the source path of the folder containing the images is chosen by the user and may vary across different PCs.
What is the proper way to deal with this?
I have found a solution.
First thing is to set
webSecurity: false
in electron's main.ts file.
Then, in the same file, register the following protocol:
app.on('ready', () => {
setTimeout(createWindow, 400);
protocol.registerFileProtocol('file', (request, callback) => {
const pathname = decodeURIComponent(request.url.replace('file:///', ''));
callback(pathname);
});
});
Finally, when using the image path, add file:// before the absolute path of the image.
This carries potential security issues, so carefully evaluate how your app is going to be used!
Related
I'm creating an Electron app and in the process am trying to use some existing javascript and other files (css and other resources). My code is spread out across a number of packages, each package containing various of these files. Outside of Electron these files would be served from a server which provides a mapping from a flat list of files to the paths to each of these files and I am trying to implement similar "server-side" functionality in Electron's "back end", if there is such a thing.
Since Electron is getting these files from the file:// protocol it is not finding most of these files because everything is resolving relative to the path of the current javascript file and these files do not know about each other and thus cannot specify a hard-coded path.
Is there some mechanism in Electron to hook requests for files so that I can provide the path for it to look at? In my server-side code I do something where I have an object which maps file names to the paths they are in, and I feel like the solution would similarly be to intercept requests and tell Electron where to look for each file.
I found this question but the solution offered there won't work for me because my web app is a bit too dynamic and the requests are coming from deep in the code, not some user interface element I could trap.
You can accomplish this by intercepting the file protocol handler. If you have set up your file mappings into an object like so:
files = {
"file1.js": "/path/to/file1.js",
"file2.js": "/path/to/file2.js",
// etc.
}
Then in the createWindow function you will insert this code immediately after you instantiate the new BrowserWindow:
protocol.interceptFileProtocol("file", (req, cb) => {
var file = req.url.split("/")
file = file[file.length-1]
if (files[file]) {
console.log(`intercepting ${file} => ${files[file]}`)
cb({path:files[file]})
}
})
Note: The protocol references a const that you get from requiring electron, e.g. something like this:
const {app, BrowserWindow, protocol} = require("electron")
This code assumes that the file names are unique and are the only part of the path that matters. So for instance, not matter what path the code thinks "file1.js" is in, in the above example it will be redirected to /path/to/file1.js. If the requested file doesn't exist then the behavior is undefined and probably nothing will load.
I'm developing a web-application with Django, which should manage and process a huge amount of user' files in local intranet. As the Django app and user files will host in the same local network, there is no necessity to upload files, it's ok to provide Django with full network path to the file via user's view.
I realised that it's impossible to get full file path from the browser due to security reasons.
There is a lot of files a user will process every day (around 150-200), so it's not ok to ask user to manually copy-paste full file path into the app. Initial design approach supposed user to drag-n-drop files from Windows Explorer to dedicated areas in the browser.
What's my options, community?
rewrite all front-end as Electron app (yikes! Just because of it!) and use Django only as REST API backend;
rewrite ducking everything as desktop app and lose all advantages Django provides (authorization, authentication, ORM, admin panel, gosh -- lots of it);
the third funny option
I feel a little stranded. Need some advice. Thanks!
I have encountered same problem while working on this.
When I think on your options
1- There is no need to re-write whole app
Create an api endpoint on server side
Create a script(program) on client that will push real paths to server
Your files should be accessible over network
Here is the script code that I used:
Tested with python Python 3.7.4 Prints realpath of all the selected files as a list.
source
import tkinter as tk
from tkinter import filedialog
import pathlib
root = tk.Tk()
root.withdraw()
root.attributes("-topmost", True)
file_path = filedialog.askopenfilenames()
# print(file_path) #debug
files = list(file_path)
print(files)
Then you need to import either a requests and generate a Json request to your server endpoint.Or just call a curl with subprocess.
2- By your definition I assume the intranet network is trusted.So is authentication necessary.And there is a question of How many users will use same file after it is processed in server.If its one then there is no need for a django app.
Well...
I've solved my problem. With much less blood than expected, which I'm happy of. :)
First, I installed Electron and one of minimal boilerplates into a new folder in my project.
In boilerplate there is a file called main.js (or index.js, depends on boilerplate), which defines Electron application window and its contents. Inside is the line which loads content to Electron BrowserWindow object:
mainWindow.loadFile(path.join(__dirname, 'index.html'));
Fortunately, BrowserWindow object does have method loadURL, which can be used to load webpage instead of local html file:
mainWindow.loadURL('http://127.0.0.1:8000');
It means that all pages rendered by Django will be shown in Electron browser, which runs node.js instead of standard browser javascript engine. Thus, this code in Django page template will work perfectly:
<h1 id="holder">DROP FILES HERE</h1>
<p id="dropped"></p>
<script>
const dropZone = document.getElementById('holder');
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
e.stopPropagation();
let filesList = '\n';
for (const f of e.dataTransfer.files) filesList += f.path + '\n';
document.getElementById('dropped').innerHTML = `Full file paths: ${filesList}`;
});
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.stopPropagation();
});
</script>
I'm generating various versions of my app in alternate languages. I'm using AOT (ahead of time) compilations, so I end up with static deployable sites in a structure that looks like this:
dist -
index.html -- entry file for the default (English language) app
-fr
index.html -- entry file for French language version
-de
index.html -- entry file for German language version
I can currently switch between the main language websites using a dropdown where the user can select their preferred language, and I then load the main entry file for the required site using plain JavaScript like this:
const baseUrl = window.location.origin;
window.location.href = baseUrl + '/' + requestedLanguage + '/index.html'; // e.g. requestedLanguage = 'fr'
This works, as it seems that requesting the actual index.html file means Angular won't interpret the request href as an Angular route.
What I want to happen though is that when the user enters a URL that already contains the language version in the path, I want that language version to be served. I also want the URL path preserved so that the Angular routing loads the appropriate component for the requested URL.
For example:
user enters myDomain.com/fr/myPage
the app under the /fr/ subdirectory is loaded, and the Angular routing in that app loads the related component for MyPage
Currently if I enter a URL myDomain.com/fr/myPage, the Angular routing tries to interpret the desired subfolder fr as a route, which doesn't exist, so I get the following error:
Error: Cannot match any routes. URL Segment: 'fr/instruments'
How can I load the required app and the correct component? There must be a way of getting Angular to recognise that the fr in the URL refers to a different app. Maybe I'm missing a build configuration or something? Here's my script in package.json for building the French language version:
"build:fr": "ng build --aot --output-path=dist/fr --base-href /fr/ --i18nFile=src/locale/messages.fr.xlf --i18nFormat=xlf --locale=fr",
just use Components instead of different separate apps and use below example
>
{path:'',component:EnHomeComponent},
{path:'contact',component:EnContactComponent},
{path:'fr',component:LayoutComponent,
children:[
{path:'',component:FrHomeComponent},
{path:'contact',component:FrContactComponent}]}
then you can directly access the pages by URL
Use angular router concept for different paths of English, France and Danish.
Then use the path based on language.
I was able to load differing language versions of the app by configuring the pipeline for my .NET back end like this, so that the Angular static index page is loaded for each different language app (referred to as 'SPA' in the code). It works by checking if there is an 'index.html' page for the first subfolder in the request URL (e.g. '/fr/', '/de/'), and it loads that page if it exists:
Startup.cs
public class Startup
{
public void Configuration()
{
app.Use((context, next) =>
{
if (!context.Request.Path.HasValue)
return next();
IFileInfo fi;
string spaIndex = "index.html";
Uri uri = new Uri(context.Request.Uri.ToString());
var segs = uri.Segments;
var folder = segs.Length > 1 ? String.Format("/{0}", segs[1]) : "/";
if (!physicalFileSystem.TryGetFileInfo(context.Request.Path.Value, out fi)) {
// check if this is a request for a sub-application, e.g an alternative language version
// if so, load the app
if (physicalFileSystem.TryGetFileInfo(String.Format("{0}{1}", folder, spaIndex), out fi))
{
context.Request.Path = new PathString(String.Format("{0}{1}", folder, spaIndex));
}
}
return next();
});
}
}
The remainder of the URL (the part after the language subfolder) is then interpreted as a route inside the app that has been loaded (e.g. the French language app). This means that the correct component is loaded for the app (I'm not entirely sure how this part is working - maybe the remainder of the path falls through to a different part of the .NET request pipeline and is passed onto the Angular app?)
I have an ng-repeat that, among other thing, outputs on image:
<div class="installation" get-products install-index="{{$index}}" ng-repeat="installation in installations track by $index">
...
<img ng-src="{{installation.logo}}" />
...
</div>
When my app starts it downloads needed images and stores their location in a local database. When the page is viewed the installations are populated:
<div class="installation ng-scope" ng-repeat="installation in installations track by $index" install-index="43" get-products="">
...
<img src="C:/Users/.../AppData/Local/Packages/.../LocalState/installations/.../...png" ng-src="C:/Users/.../AppData/Local/Packages/.../LocalState/installations/.../...png">
...
</div>
(dots used to hide person and client data)
If I paste the src location into my browser I see the image so I know it's saved at that location. However, in my app it's not showing. This is a constant issue through the app with the downloaded files. I know the image are in the correct area and the src location is correct but none of them show.
--- EDIT ---
I do have white listing applied as I was getting an unsafe for file:///. Also, when I was using a relative path it was working fine. I had a preloaded database that pointed to file inside the app files.
I don't think it's an access issue since I have a .db file at the same location that all my data is being pulled from.
--- EDIT ---
I set it as file:///C:/... and I'm having the same issue.
I also tried file:///C:/... , http://localhost/..., http://localhost:/..., http://localhost:C/..., C:/..., and file:///.... None of witch give me anything. The first two localhost items do give me a broken image icon, that's about it. I'm not running a local server, just thought I'd try it.
You can do this in two different ways:
1) Use the file protocol
2) use a local host server to store the picture and access it from the local host
for security reasons you cannot use your file system path for images. you shouldn't even use it at all, because when your app gets hosted, you wouldn't be accessing the image via such paths.
method 1:
just add file:/// in place of the c:/. file is the protocol for your file system, just as http or HTTPS is a web protocol.
NB: I haven't tested or used this before so I'm not really certain. I'm posting this from a small mobile device. but I believe it should work.
method 2:
start your wampserver or python server or any local server you have. put the image in a folder where your server can access (if wampserver, this would be a folder or directory in your WWW). say the name of the folder is "my_images" and your wampserver is running on localhost.. you can access the image like so:
http://localhost/my_images/image_name
use this path for your ng-src.
Because I Cordova File and Windows weren't playing nice using the call for cordova.file.dataDirectory didn't work. Instead I used the fs object returned by window.requestFileSystem(...,function(fs){...});
When generating my save to path as well as the path to create directories and location data I used fs.winpath which returned C:/.... The web (which Cordova basically is) won't allow you to have access to local files not associated with the site/apps structure, which is now obvious.
I dug in to the fs object and found fs.root.nativeURL points to ms-appdata:///local/. Switching everything over to this still downloaded all files and directories to the same location but stored that to the database as the file location. When the app loaded the ms-appdata path instead of the C:/ path the images displayed.
oh, a Cordova app.. why don't you place the file in an images folder In your project. since all files will be loaded using index.html (I assume). you can easily refer to the file relative to the location of index.html. how I would normally organize my project is that, my index.html and folders containing resources like js, CSS etc would be on thesame level, so I can easily get the image files using ng-src="img/image_name". so I could have a structure like this
index.HTML
img
..image_name.ext
..image2.ext
css
..style.css test it in a browser location if it works, it will work on the device. Cordova would know how to translate d into something it can recognise.
This is some sample code, i quickly put together. I tested it and it worked. Firstly i create a directory using file plugin and then download to this directory using file transfer. Replace the url parameter of file transfer with the url you wish to download from.
$ionicPlatform.ready(function() {
$cordovaFile.createDir(cordova.file.externalDataDirectory,
file_location,false).then(
function(success){
return success;
},function(error){
return error;
}).then(function(value){
var url = material.file_uri;
var targetPath = cordova.file.externalDataDirectory
+ "/" +file_location + "/" + file_name;
var trustHosts = true
var options = {};
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
.then(function(result) {
console.log(result)
}, function(err) {
console.log(err)
}, function (progress) {
$timeout(function () {
console.log(Math.floor((progress.loaded / progress.total) * 100));
})
});
})
})
When a meteor app gets compiled (meteor build app), the public directory becomes \programs\web.browser\app All files which were in the development public directory \public are now accessible with http://domain.tld/file-in-public-directory.jpg
When I put a new file into the compiled public directory and try to view it in the browser, I get an error, which says that Meteor does not have a route for that url. When I do this in the development public directory it works flawless, but not in the compiled (meteor build app).
I need this, because I want to upload new files in that directory.
Any help?
So, you'll have to tweak it a little bit, but there's a way you can access pretty much any folder you want, especially for static files, by using the low level connectHandlers object.
Here is an example where you have a folder named .images (hidden from the Meteor auto-refresh), which serves images whenever a request is made to http://[yoursiteURL]/images/...
var fs = Npm.require('fs');
WebApp.connectHandlers.use(function(req, res, next) {
var re = /^\/images\/(.*)$/.exec(req.url);
if (re !== null) {
var filePath = process.env.PWD
+ '/.images/'
+ re[1];
var data = fs.readFileSync(filePath, data);
res.writeHead(200, {
'Content-Type': 'image'
});
res.write(data);
res.end();
} else {
next();
}
});
You're using a regular expression to find out if the incoming request is trying to access /images/. If it is, we're going to send the image with the appropriate headers, using res.write()
Two things of note:
1- you don't have to use anything special (no packages, etc) to use the Npm.require('fs') because it's built in and usable.
2- using fs.readFileSync is a bit of a hack, and is blocking. You'll want to tweak that for a performant, production app.
Hope this helps! A bit more information on connectHandlers can be found here.
Nice to see folks out there trying meteor. It's great stuff - but at the same time, it does seem complicated. What really helped me so much is using this app: metoer-kitchen. I now use it alongside when working on my projects.