20

I'm buildind now GUI using Electron. (like PhoneGap for desktop apps)

Is there a way to enable full path for file checked in <input type="file">?
Insted of C:\fakepath\dataset.zip now. (the directory name isn't "fakepath", but that is the value of document.getElementById("myFile").value)

Or, is there other way to select a file?

Dani-Br
  • 2,289
  • 5
  • 25
  • 32
  • 2
    `C:\fakepath\dataset.zip` is a full path. What do you mean? – Henrique Barcelos Jul 22 '16 at 15:37
  • No, the directory name isn't `fakepath`, but that exactly what I get from `document.getElementById("myFile").value`. – Dani-Br Jul 22 '16 at 15:59
  • You can't get input files like this. – Henrique Barcelos Jul 22 '16 at 17:01
  • 1
    @HenriqueBarcelos -- Not in end-user browsers, but you can do this in runtime environments with elevated privileges, such as Electron. Also, `c:\fakepath\` is not a full path, it's a well known [faked path](https://davidwalsh.name/fakepath), which is there for legacy reasons. – John Weisz Feb 08 '17 at 16:55

6 Answers6

62

Electron adds a path property to File objects, so you can get the real path from the input element using:

document.getElementById("myFile").files[0].path
Vadim Macagon
  • 14,463
  • 2
  • 52
  • 45
2

It is not possible to do what you are trying for security reasons, according this answer How to get full path of selected file on change of <input type=‘file’> using javascript, jquery-ajax?.

However you could do a work around like I did in an electron project I worked on.

  1. Create a HTML button
  2. Then in the renderer process create an event listener to the button you created before.

    const ipc = require('electron').ipcRenderer;
    const buttonCreated = document.getElementById('button-created-id');
    
    buttonCreated.addEventListener('click', function (event) {
        ipc.send('open-file-dialog-for-file')
    });
    
  3. Then in the main process you use the showOpenDialog to choose a file and then send the full path back to the renderer process.

    ipc.on('open-file-dialog-for-file', function (event) {
     if(os.platform() === 'linux' || os.platform() === 'win32'){
        dialog.showOpenDialog({
            properties: ['openFile']
        }, function (files) {
           if (files) event.sender.send('selected-file', files[0]);
        });
    } else {
        dialog.showOpenDialog({
            properties: ['openFile', 'openDirectory']
        }, function (files) {
            if (files) event.sender.send('selected-file', files[0]);
        });
    }});
    
  4. Then in the renderer process you get the full path.

    ipc.on('selected-file', function (event, path) {
        console.log('Full path: ', path);
    });
    

Thus you can have a similar behaviour than the input type file and get the full path.

Community
  • 1
  • 1
Piero Divasto
  • 1,065
  • 13
  • 21
  • 2
    *"It is not possible to do what you are trying for security reasons"* -- This is true for end-user browsers, but not Electron. I believe you can simply use `input.files[0].path`, which is also described in the [currently accepted answer](http://stackoverflow.com/a/38549837/2788872). – John Weisz Feb 08 '17 at 16:59
2
<script>

    const electron = require('electron');
    const { ipcRenderer } = electron;
    const ko = require('knockout')
    const fs = require('fs');
    const request = require('request-promise');

    // replace with your own paths
    var zipFilePath = 'C:/Users/malco/AppData/Roaming/Wimpsdata/Wimpsdata.zip';
    var uploadUri = 'http://localhost:59887/api/Collector/Upload'

    var request = require('request');
    request.post({
        headers: { 'content-type': 'application/zip' },
        url: uploadUri,
        body: fs.createReadStream(zipFilePath)
    }, function (error, response, body) {
        console.log(body);
        location.href = 'ScanResults.html';
    });
</script>

ASP .NET WebAPI Conontroller

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using Wimps.Services.Business;

namespace Wimps.Services.Controllers
{
    public class CollectorController : ApiController
    {

        public async Task<bool> Upload()
        {
            try
            {
                var fileuploadPath = ConfigurationManager.AppSettings["FileUploadLocation"];

                var provider = new MultipartFormDataStreamProvider(fileuploadPath);
                var content = new StreamContent(HttpContext.Current.Request.GetBufferlessInputStream(true));
                foreach (var header in Request.Content.Headers)
                {
                    content.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }

                Byte[] byteArray = await content.ReadAsByteArrayAsync();

                string newFileName = Guid.NewGuid().ToString();
                string newFilePath = fileuploadPath + "\\" + newFileName + ".zip";
                if (File.Exists(newFilePath))
                {
                    File.Delete(newFilePath);
                }

                File.WriteAllBytes(newFilePath, byteArray);

                string unzipTo = fileuploadPath + "\\" + newFileName;
                Directory.CreateDirectory(unzipTo);

                DirectoryInfo di = new DirectoryInfo(unzipTo);
                foreach (FileInfo file in di.GetFiles())
                {
                    file.Delete();
                }

                ZipFile.ExtractToDirectory(newFilePath, unzipTo);



                return true;
            }
            catch (Exception e)
            {
                // handle exception here
                return false;
            }
        }
    }
}

Need to add key to web config for file upload

<configuration>
  <appSettings>
... other keys here
    <add key="FileUploadLocation" value="C:\Temp\Uploads" />
  </appSettings>

rest of app config ... ...

Malcolm Swaine
  • 109
  • 1
  • 3
1

The accepted answer works great for the original question, but the answer from @Piero-Divasto works a lot better for my purposes.

What I needed was the pathname of a directory which may be rather large. Using the accepted answer, this can block the main process for several seconds while it processes the directory contents. Using dialog.showOpenDialog(...) gets me a near-instant response. The only difference is that dialog.showOpenDialog doesn't take a callback function anymore, and instead returns a promise:

ipcMain.on("open-file-dialog-for-dir", async event => {
  const dir = await dialog.showOpenDialog({ properties: ["openDirectory"] });
  if (dir) {
    event.sender.send("selected-dir", dir.filePaths[0]);
  }
});
0
<script>const electron = require('electron');</script>
<button id="myFile" onclick="this.value=electron.remote.dialog.showOpenDialog()[0]">UpdateFile</button>

Now, the document.getElementById("myFile").value would contain the full path of the chosen file.

Dani-Br
  • 2,289
  • 5
  • 25
  • 32
0

As answered by Vadim Macagon:

let { path } = document.getElementById("myFile").files[0]

Since there is no included interface for this for TypeScript as of this answer, to use this you have to cast the File to another type

let { path } = document.getElementById("myFile").files[0] as any

or, if you would rather not use any

interface ElectronFile extends File {
    path: string;
}

let { path } = document.getElementById("myFile").files[0] as ElectronFile
ZomoXYZ
  • 1,763
  • 21
  • 44