Electron, v3

File System Dialog & Reading Files

Steve Kinney

Steve Kinney

Electron, v3

Check out a free preview of the full Electron, v3 course

The "File System Dialog & Reading Files" Lesson is part of the full, Electron, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Steve demonstrates triggering a file system dialog by utilizing Electron's dialog module. How to restrict the allowed file types and reading files using fs/promises is also covered in this segment.


Transcript from the "File System Dialog & Reading Files" Lesson

>> With that, right? I think it would be interesting to look at what it would take to actually trigger a file system dialog or something along those lines and play around with that. Now, eventually where we choose to go with this towards the end of this little section together is I would like it when I hit the open file button to do it.

All right, but we'll get there cuz we're still somewhat new at the bridging the gap, if you will, between the main process and the renderer process. So let's kind of just start by, cool, can I do an OS thing from inside of electron? And then we'll get the two way communication of when they click this button, then tell the main process, I would like a file open dialog, let them pick the file, read the file in the main process, send me the content over to the render process so I can display it in the DOM, right?

That's kind of the first exercise we're gonna kind of do together as a group is just kind of getting that full flow from interaction to file system, access, back into rendering into the DOM, right? So that kind of first mission together. Like I said, there are a whole bunch of modules in here that we can pull from and use.

One of the ones we're gonna need in this case is our good friend, the dialog module, Right? And so dialog module is the ability to open dialogs, file, save dialogs, so on so forth. And the ability to customize them in a lot of interesting ways as well. And so we gonna just give ourselves some room down here, and we'll go ahead and we will say something along the lines of, showOpenDialog, right?

And that's gonna, yeah, it'll be an async function cuz, again, most of these things these days will return promises, which means we can use them with async await. So in that case we can go ahead pull this down and then we'll say, yeah, this is the result and so we'll await this dialog.showOpenDialog.

And there's a bunch of properties you can give this, right? See if we can actually look at the TypeScript answers here, right? And so you can configure it and customize it and a lot of ways you might not be able to normally do in Chromium or in just regular Chrome in the browser, right?

When we want an open file, you can let them open an entire directory. Do we want to let them pick more than one file? Show hidden files? Do I give them the ability to create a directory? You have a lot of abilities to customize this. Do not add to recent.

A bunch of different options here where you have a lot more control than you would ever have, there are a lot of these. It's one of those things where there's a lot of component parts of electron and at the same time you get a bunch of new browser API's that also give you a bunch of cool stuff, right?

But always I think the full level cuz these are hooking into a lot of the MacOS and Windows frameworks directly. And so you will always get a little bit more customization than you get in the browser as well. So here we're just saying one for just open a file and then whatever they choose will be saved in this result.

And we will wait until we actually trigger one of these to kind of see what that looks like. So, at this point we're just gonna ask them to pick a file and then we're just going to see what we get back from that module, right? And so what I'll probably do in this case is I'm just gonna say showOpenDialog, which is kind of where it means the app will start up,

and immediately, as soon as it's got the focus, ask me for what kind of file I wanna open, and like I said before. We just know that it will console log it, right? Cool, so let's go ahead and I'm going to then just kill this and restart it And you can see that I get a file open dialog here as well, right?

And I can pick a file, we'll go ahead and grab this and you can see that we get this array. We did not cancel. That's important cuz they could choose to not open a file, right? And then we get an array of the files that they selected. Now obviously an array, because we saw that we had the ability, we can turn off or turn on the ability to select multiple files, right?

In this case, and I will kinda kill this app and restart it again. You will see that I cannot pick, you'll see that I have not picked more than one. I know that I'm trying to hold Shift and click on the second one, but I can't pick more than one file in this case.

And so then we have that we can see, the canceled is false and we have the directory of files, right? And so we've gotten an initial piece in here as well. We can also do something really cool, which is, this application supports rendering markdown files as HTML. So do I want to let them open an image file?

Probably not, right? I probably wanna scope it only to markdown files. And so what I'm able to do there is I can actually give it this filters, right? I can actually say, we will say name, and again, this name Markdown Files is actually, you will not see it on MacOS, you will only see that one on Windows where it's, where you pick the file type or something along those lines.

That is not something that will appear on MacOS, that'll only appear on Windows. So some of these things are a little bit platform specific, that said, for a lot of the ones that we're gonna do the sheer amount of code that I have to write for some of these platform specific niceties is very little, this ain't bothering anyone even if we only use it on MacOS.

In fact, we can argue that it's somewhat at least adding clarity to my code. All right, it's not the worst thing in the world. And we got that in place. And again, if you wanna also say dot mark down, yeah, put it whatever you want in there, right?

It doesn't matter. That'll all work. And then let's go ahead, I'm just going to take a screenshot, for instance. And we'll go ahead and we'll drag it into this important documents folder as well, so that we can verify that screen shot. Cool, so now I've got that filter in place.

Let's go ahead and we'll start our code back up. Boom, we'll go over to our app. And if I go into that important documents, I cannot pick the screenshot, right? Cuz I've said only things that end in a dot MD that are marked down files. Yeah, theoretically, could I allow text files?

Yeah, I could allow all those things, right? But for the most part, I can get very specific about what they can and cannot open. I don't necessarily have to have very defensive code later to make sure there's a file that I know about. And yeah, arguably, maybe you should still have the defensive code just in case, but the idea that you can have full control of this window I think is incredibly powerful as well.

Great, and as you can see that time I cancelled it at this point I get cancelled as true, and I don't think about it. So then we come down to the fact that we have a file path, right? So then the next step is, I guess, to read the file.

Does anyone wanna take a wild guess on how to read a file? It's just a node library for reading a file, you just have access to node.
>> FS.
>> FS, I am a gentleman, so for me it's gonna be FS promises. In this case we'll do read file, and there's again, like if you said open directory, you could do the read outer.

You have all of the options to read files in node available to you. And if you want to use any of the other libraries that are abstractions around to the FS library node like any other file system like replacement libraries, those all work to. And this is a node process.

Anything like new node you can do here, you're just running a predetermined version of node, given the electron version that you're running and which and yeah, again, considering this is electron 26, it is most likely maybe one minor version or patch version back or something along those lines.

So we could theoretically, we know that if the result is canceled, just return, I don't want to do anything. And then we'll say give me the first filePath from all those filePaths. And again, these are mostly somewhat both defensive coding and then also some of us here need to appease TypeScript as well.

But I think it assumes that if I haven't canceled it, that I have at least one filePath, so that's good. So we can do content equals readFile or await readFile and read that file and we should. Let's go ahead and we'll console log both, the content and the result in this case.

Cool, I'll go ahead and I will go grab a file As you can see, the content is the mark down that is in the file. The result is what we saw previously, right? Now I have the ability to trigger a file system dialog from my UI, then have that determine which one node is gonna read from and read the file.

I am getting dangerously close to actually getting it into the render context at this point, and so there's a few little slight tweaks that I might choose to do here. That I'm just gonna kinda refactor it slightly because I know future me is gonna want some of these refactors, right?

>> Is there a way to get this code to load without having to restart the app every time?
>> You can try to use nodemon or something along those lines any of the things that will automatically restart the code every single time, right? Will work as well. In so far that I am triggering file system dialogs, and stuff along those lines right now, I just chose to keep my life simple, and be manual about it, but yeah, your client side code with Vite or Webpack will automatically do the hot module reloading.

Your server side code, the same way you can automatically restart any node server, if you like nodemon great, if there's some other library you'd like to use that will work as well. Those are options you can either, NPM, install dash D them and then use an NPX or you can just globally install those and choose to nodemon this instead of NPM start that will work for you as well.

I am personally going to choose not to. I just don't need to add extra chaos elements while I'm live coding, right? Cuz no one wants me to stop and debug if I have some kind of boo boo along those lines. So yeah, so the one thing that I just wanna do here as a quick refactor is just separate out the idea of choosing a file and opening a file.

Because we know that right now, I can trigger showOpenDialog, but eventually on a long enough timeline, and yeah, we won't get to all of these today. But there are other ways to open a file, right? There's dragging it into the window, right? And you might choose to use that API that's available in the browser to communicate that you wanna open a file.

There's using a file open menu, right? For a given window or something along those lines. There's a ton of different ways to do this, right? And so I just don't necessarily want to have both of those pieces of functionality necessarily tied to each other. And we'll see why in a little bit.

So I'm just gonna separate out the idea of opening a file. And here we'll just say that opening file will take some kind of filePath that is gonna be a string. And again this that is the level of TypeScript that I'm dealing with right now. It's just saying that my argument is a string and getting that safety.

Other than that we've been doing TypeScript the entire time, and you haven't necessarily noticed. So we got that in place. And then what we'll do is we'll just kind of start by pulling this out. And we'll just say, we might even choose to return the filePath in the showOpenDialog.

I don't know necessarily what I want totally just yet. But we'll figure it out as we get there, right? So basically the same code except now we've just separated it out just a teeny bit as well. There are a few other niceties and this one is a "MacOS only" one.

But like I said it won't do anything different on Windows, you will see no difference in the same way this didn't do anything on MacOS. Everyone's gonna get some niceties for themselves is that it is less apparent in modern versions of MacOS, but if you've been on Mac a little bit, remember when it used to have that whole animation where it would come down from the title bar in an accordion or whatever, like a curtain that was coming down, right?

There's a way to get that effect as well. Right now, my showOpenDialog just kind of pops in front of the window. We can have it effectively attached to the window as well. And because it is shockingly easy to do, and I will also need this functionality later, we're gonna do it.

So we'll just say that our showOpenDialog will take some kind of browser window, which is gonna be a browser window. Again, that is the amount of TypeScript that I need to deal with in my life right now. And we can pass it in as the first argument to the showOpenDialog.

And what that does effectively on MacOS and MacOS only will effectively attach showOpenDialog to that window, right? And again, if you're running a slightly older version of MacOS, this will be more dramatic than what we're about to see. But it's still a present and real thing. See this is why I use TypeScript because I was about to go and look at it and it was not gonna work because I did not pass in that mainWindow to that function.

I was why do I have a red dot next to my index.ts is because my code was broken and because types are good. So with that, We can start it up. Now you can see that it is kinda attached this window and as I move this window around, it's not the same dramatic fully attached sheet that it was in previous versions of MacOS but it is now very much attached to this window.

If you are Windows or Linux you notice nothing. In the same way that when we put that markdown files in the filters those of us on MacOS notice nothing, everyone gets to notice nothing in a different sense. But those little accoutrements are the things that separate this feeling, some janky app, why did I download and install this on my computer to a thing that fits into that OS, right?

And we are somewhat lucky because we're so used to web apps that we don't necessarily have to, are there UI libraries that seek to make the buttons look like MacOS or Windows buttons using CSS there are. I sense my personal belief system is that there's an uncanny valley there, which is if you try to make it look exactly like MacOS using CSS, you will get 80% there and that is worse than not trying.

But generally speaking, when we can get some of these nice things, we'll see some other things we can do along the way, we should go for it because it's good. So we've got that there as well.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now